
META-INF.source.com.novell.ldapchai.impl.edir.entry.EdirEntries Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ldapchai Show documentation
Show all versions of ldapchai Show documentation
The LDAP Chai API, easy to use LDAP for Java developers.
The newest version!
/*
* LDAP Chai API
* Copyright (c) 2006-2017 Novell, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.novell.ldapchai.impl.edir.entry;
import com.novell.ldapchai.ChaiConstant;
import com.novell.ldapchai.ChaiEntry;
import com.novell.ldapchai.ChaiEntryFactory;
import com.novell.ldapchai.ChaiGroup;
import com.novell.ldapchai.ChaiPasswordPolicy;
import com.novell.ldapchai.ChaiPasswordRule;
import com.novell.ldapchai.ChaiUser;
import com.novell.ldapchai.exception.ChaiOperationException;
import com.novell.ldapchai.exception.ChaiUnavailableException;
import com.novell.ldapchai.impl.edir.entry.ext.GetPwdPolicyInfoRequest;
import com.novell.ldapchai.impl.edir.entry.ext.GetPwdPolicyInfoResponse;
import com.novell.ldapchai.provider.ChaiProvider;
import com.novell.ldapchai.provider.ChaiSetting;
import com.novell.ldapchai.provider.SearchScope;
import com.novell.ldapchai.util.internal.ChaiLogger;
import com.novell.ldapchai.util.DefaultChaiPasswordPolicy;
import com.novell.ldapchai.util.SearchHelper;
import com.novell.ldapchai.util.internal.StringHelper;
import javax.naming.ldap.ExtendedResponse;
import java.math.BigInteger;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* A collection of static helper methods used by the LDAP Chai API.
* Generally, consumers of the LDAP Chai API should avoid calling these methods directly. Where possible,
* use the {@link com.novell.ldapchai.ChaiEntry} wrappers instead.
*
* @author Jason D. Rivard
*/
public class EdirEntries
{
private static final String EDIR_TIMESTAMP_PATTERN = "yyyyMMddHHmmss'Z'";
private static final DateTimeFormatter EDIR_TIMESTAMP_FORMATTER = new DateTimeFormatterBuilder()
.appendPattern( EDIR_TIMESTAMP_PATTERN )
.toFormatter();
private static final ChaiLogger LOGGER = ChaiLogger.getLogger( EdirEntries.class );
static final String EDIR_ATTR_SUBORDINATE_COUNT = "subordinateCount";
/**
* Convert a Instant to the Zulu String format.
* See the eDirectory Time attribute syntax definition for more details.
*
* @param instant The Date to be converted
* @return A string formatted such as "199412161032Z".
*/
public static String convertInstantToZulu( final Instant instant )
{
if ( instant == null )
{
throw new NullPointerException();
}
try
{
return EDIR_TIMESTAMP_FORMATTER.format( instant.atZone( ZoneOffset.UTC ) );
}
catch ( DateTimeParseException e )
{
throw new IllegalArgumentException( "unable to format zulu time-string: " + e.getMessage() );
}
}
/**
* Convert the commonly used eDirectory zulu time string to java Date object.
* See the eDirectory Time attribute syntax definition for more details.
*
* @param input a date string in the format of "yyyyMMddHHmmss'Z'", for example "19941216103200Z"
* @return A Date object representing the string date
* @throws IllegalArgumentException if dateString is incorrectly formatted
*/
public static Instant convertZuluToInstant( final String input )
{
if ( input == null )
{
throw new NullPointerException();
}
try
{
final LocalDateTime localDateTime = LocalDateTime.parse( input, EDIR_TIMESTAMP_FORMATTER );
final ZonedDateTime zonedDateTime = localDateTime.atZone( ZoneOffset.UTC );
return Instant.from( zonedDateTime );
}
catch ( DateTimeParseException e )
{
throw new IllegalArgumentException( "unable to parse zulu time-string: " + e.getMessage() );
}
}
static boolean convertStrToBoolean( final String string )
{
return StringHelper.convertStrToBoolean( string );
}
static int convertStrToInt( final String string, final int defaultValue )
{
if ( string == null )
{
return defaultValue;
}
try
{
return Integer.parseInt( string );
}
catch ( Exception e )
{
return defaultValue;
}
}
/**
* Creates a new user entry in the ldap directory. A new "inetOrgPerson" object is created in the
* ldap directory. Generally, calls to this method will typically be followed by a call to the returned
* {@link com.novell.ldapchai.ChaiUser}'s write methods to add additional data to the ldap user entry.
*
* @param userDN the new userDN.
* @param sn the last name of
* @param provider a ldap provider be used to create the group.
* @return an instance of the ChaiUser entry
* @throws com.novell.ldapchai.exception.ChaiOperationException If there is an error during the operation
* @throws com.novell.ldapchai.exception.ChaiUnavailableException If the directory server(s) are unavailable
*/
public static ChaiUser createUser( final String userDN, final String sn, final ChaiProvider provider )
throws ChaiOperationException, ChaiUnavailableException
{
final Map createAttributes = new HashMap<>();
createAttributes.put( ChaiConstant.ATTR_LDAP_SURNAME, sn );
provider.createEntry( userDN, ChaiConstant.OBJECTCLASS_BASE_LDAP_USER, createAttributes );
//lets create a user object
return provider.getEntryFactory().newChaiUser( userDN );
}
private static ChaiEntry findPartitionRoot( final ChaiEntry theEntry )
throws ChaiUnavailableException, ChaiOperationException
{
ChaiEntry loopEntry = theEntry;
while ( loopEntry != null )
{
final Set objClasses = loopEntry.readMultiStringAttribute( ChaiConstant.ATTR_LDAP_OBJECTCLASS );
if ( objClasses.contains( ChaiConstant.OBJECTCLASS_BASE_LDAP_PARTITION ) )
{
return loopEntry;
}
loopEntry = loopEntry.getParentEntry();
}
return null;
}
/**
* Find the appropriate password policy for the given user. The ChaiProvider
* must have appropriate rights to read the policy and policy assignment attributes.
*
* @param theUser The user to find the the password policy for
* @return A valid PasswordPolicy for the user
* @throws com.novell.ldapchai.exception.ChaiUnavailableException If the ldap server(s) are not available
* @throws com.novell.ldapchai.exception.ChaiOperationException If there is an error during the operation
* @see com.novell.ldapchai.ChaiUser#getPasswordPolicy()
*/
public static ChaiPasswordPolicy readPasswordPolicy( final ChaiUser theUser )
throws ChaiUnavailableException, ChaiOperationException
{
return UserPasswordPolicyReader.readPasswordPolicy( theUser );
}
/**
* Remove a group membership for the supplied user and group. This implementation
* takes care of all four attributes used in eDirectory static group associations:
*
* - {@link com.novell.ldapchai.ChaiConstant#ATTR_LDAP_GROUP_MEMBERSHIP}
* - {@link com.novell.ldapchai.ChaiConstant#ATTR_LDAP_MEMBER}
* - {@link com.novell.ldapchai.ChaiConstant#ATTR_LDAP_SECURITY_EQUALS}
* - {@link com.novell.ldapchai.ChaiConstant#ATTR_LDAP_EQUIVALENT_TO_ME}
*
*
* @param user A valid {@code ChaiUser}
* @param group A valid {@code ChaiGroup}
* @throws com.novell.ldapchai.exception.ChaiUnavailableException If the ldap server(s) are not available
* @throws com.novell.ldapchai.exception.ChaiOperationException If there is an error during the operation
*/
public static void removeGroupMembership( final ChaiUser user, final ChaiGroup group )
throws ChaiOperationException, ChaiUnavailableException
{
if ( user == null )
{
throw new NullPointerException( "user cannot be null" );
}
if ( group == null )
{
throw new NullPointerException( "group cannot be null" );
}
//Delete the attribs off of the user
user.deleteAttribute( ChaiConstant.ATTR_LDAP_GROUP_MEMBERSHIP, group.getEntryDN() );
user.deleteAttribute( ChaiConstant.ATTR_LDAP_SECURITY_EQUALS, group.getEntryDN() );
//Delete the attribs off of the group
group.deleteAttribute( ChaiConstant.ATTR_LDAP_MEMBER, user.getEntryDN() );
group.deleteAttribute( ChaiConstant.ATTR_LDAP_EQUIVALENT_TO_ME, user.getEntryDN() );
}
/**
* Add a group membership for the supplied user and group. This implementation
* takes care of all four attributes used in eDirectory static group associations:
*
* - {@link com.novell.ldapchai.ChaiConstant#ATTR_LDAP_GROUP_MEMBERSHIP}
* - {@link com.novell.ldapchai.ChaiConstant#ATTR_LDAP_MEMBER}
* - {@link com.novell.ldapchai.ChaiConstant#ATTR_LDAP_SECURITY_EQUALS}
* - {@link com.novell.ldapchai.ChaiConstant#ATTR_LDAP_EQUIVALENT_TO_ME}
*
*
* @param user A valid {@code ChaiUser}
* @param group A valid {@code ChaiGroup}
* @throws com.novell.ldapchai.exception.ChaiUnavailableException If the ldap server(s) are not available
* @throws com.novell.ldapchai.exception.ChaiOperationException If there is an error during the operation
*/
public static void writeGroupMembership( final ChaiUser user, final ChaiGroup group )
throws ChaiOperationException, ChaiUnavailableException
{
if ( user == null )
{
throw new NullPointerException( "user cannot be null" );
}
if ( group == null )
{
throw new NullPointerException( "group cannot be null" );
}
user.addAttribute( ChaiConstant.ATTR_LDAP_GROUP_MEMBERSHIP, group.getEntryDN() );
user.addAttribute( ChaiConstant.ATTR_LDAP_SECURITY_EQUALS, group.getEntryDN() );
group.addAttribute( ChaiConstant.ATTR_LDAP_MEMBER, user.getEntryDN() );
group.addAttribute( ChaiConstant.ATTR_LDAP_EQUIVALENT_TO_ME, user.getEntryDN() );
}
private EdirEntries()
{
}
private static final class UserPasswordPolicyReader
{
private static final Set TRADITIONAL_PASSWORD_ATTRIBUTES;
private static final SearchHelper NSPM_ENTRY_SEARCH_HELPER = new SearchHelper();
static
{
{
final Set tempSet = new HashSet<>();
tempSet.add( ChaiConstant.ATTR_LDAP_PASSWORD_MINIMUM_LENGTH );
tempSet.add( ChaiConstant.ATTR_LDAP_PASSWORD_EXPIRE_INTERVAL );
tempSet.add( ChaiConstant.ATTR_EDIR_PASSWORD_POLICY_PASSWORD_UNIQUE_REQUIRED );
TRADITIONAL_PASSWORD_ATTRIBUTES = Collections.unmodifiableSet( tempSet );
}
final Set nspmPasswordAttributes;
{
final Set tempSet = new HashSet<>();
for ( final NspmPasswordPolicy.Attribute attr : NspmPasswordPolicy.Attribute.values() )
{
tempSet.add( attr.getLdapAttribute() );
}
nspmPasswordAttributes = Collections.unmodifiableSet( tempSet );
}
{
NSPM_ENTRY_SEARCH_HELPER.setSearchScope( SearchScope.BASE );
NSPM_ENTRY_SEARCH_HELPER.setAttributes( nspmPasswordAttributes );
}
}
static ChaiPasswordPolicy readPasswordPolicy( final ChaiUser theUser )
throws ChaiUnavailableException, ChaiOperationException
{
ChaiPasswordPolicy pwordPolicy = DefaultChaiPasswordPolicy.createDefaultChaiPasswordPolicy();
boolean usedUniversalPolicy = false;
try
{
// fetch the user's associated password policy
final ChaiEntry policyEntry = findNspmPolicyForUser( theUser );
if ( policyEntry != null )
{
pwordPolicy = new NspmPasswordPolicyImpl( policyEntry.getEntryDN(), policyEntry.getChaiProvider() );
// check to see if the advanced rules on the password policy are "enabled"
if ( pwordPolicy.getRuleHelper().isPolicyEnabled() )
{
// we've got a policy, and rules are enabled, now read the policy.
LOGGER.trace( () -> "using active universal password policy for user " + theUser.getEntryDN() + " at " + policyEntry.getEntryDN() );
usedUniversalPolicy = true;
}
else
{
LOGGER.debug( () -> "ignoring unenabled nspm password policy for user " + theUser.getEntryDN() + " at " + policyEntry.getEntryDN() );
}
}
}
catch ( ChaiOperationException e )
{
LOGGER.error( () -> "ldap error reading universal password policy: " + e.getMessage() );
throw e;
}
// if there is no universal password policy then fall back to reading user object attrs
if ( !usedUniversalPolicy )
{
try
{
pwordPolicy = readTraditionalPasswordRules( theUser );
LOGGER.trace( () -> "read traditional (non-nmas) password attributes from user entry " + theUser.getEntryDN() );
}
catch ( ChaiOperationException e )
{
LOGGER.error( () -> "ldap error reading traditional password policy: " + e.getMessage() );
}
}
return pwordPolicy;
}
private static ChaiEntry findNspmPolicyForUser( final ChaiUser theUser )
throws ChaiUnavailableException, ChaiOperationException
{
final boolean useNmasSetting = theUser.getChaiProvider().getChaiConfiguration().getBooleanSetting( ChaiSetting.EDIRECTORY_ENABLE_NMAS );
final ChaiEntryFactory chaiEntryFactory = theUser.getChaiProvider().getEntryFactory();
if ( useNmasSetting )
{
final GetPwdPolicyInfoRequest request = new GetPwdPolicyInfoRequest();
request.setObjectDN( theUser.getEntryDN() );
final ExtendedResponse response = theUser.getChaiProvider().extendedOperation( request );
if ( response != null )
{
final GetPwdPolicyInfoResponse polcyInfoResponse = ( GetPwdPolicyInfoResponse ) response;
final String policyDN = polcyInfoResponse.getPwdPolicyDNStr();
if ( policyDN != null )
{
return chaiEntryFactory.newChaiEntry( policyDN );
}
}
return null;
}
else
{
// look at user object first
{
final String policyDN = theUser.readStringAttribute( ChaiConstant.ATTR_EDIR_PASSWORD_POLICY_DN );
if ( policyDN != null && policyDN.length() > 0 )
{
return chaiEntryFactory.newChaiEntry( policyDN );
}
}
final ChaiEntry parentObject = theUser.getParentEntry();
// look at parent next
{
if ( parentObject != null )
{
final String policyDN = parentObject.readStringAttribute( ChaiConstant.ATTR_EDIR_PASSWORD_POLICY_DN );
if ( policyDN != null && policyDN.length() > 0 )
{
return chaiEntryFactory.newChaiEntry( policyDN );
}
}
}
// look at partition root
{
if ( parentObject != null )
{
final ChaiEntry partitonRoot = findPartitionRoot( parentObject );
if ( partitonRoot != null )
{
final String policyDN = partitonRoot.readStringAttribute( ChaiConstant.ATTR_EDIR_PASSWORD_POLICY_DN );
if ( policyDN != null && policyDN.length() > 0 )
{
return chaiEntryFactory.newChaiEntry( policyDN );
}
}
}
}
// look at policy object
{
final ChaiEntry securityContainer = chaiEntryFactory.newChaiEntry( "cn=Security" );
final String loginPolicyDN = securityContainer.readStringAttribute( "sASLoginPolicyDN" );
if ( loginPolicyDN != null && loginPolicyDN.length() > 0 )
{
final ChaiEntry loginPolicy = chaiEntryFactory.newChaiEntry( loginPolicyDN );
final String policyDN = loginPolicy.readStringAttribute( ChaiConstant.ATTR_EDIR_PASSWORD_POLICY_DN );
if ( policyDN != null && policyDN.length() > 0 )
{
return chaiEntryFactory.newChaiEntry( policyDN );
}
}
}
}
return null;
}
/**
* Reads and applies the user's traditional (non-UP) password rules to the policy.
*
* If the user does not have an associated universal password policy, this
* method can be used to read the old style password rules.
*
* @param theUser a valid chaiUser instance
* @return a PasswordPolicy instance representing the users policy
* @throws ChaiUnavailableException If the ldap server(s) are not available
* @throws ChaiOperationException If there is an error during the operation
*/
private static ChaiPasswordPolicy readTraditionalPasswordRules( final ChaiUser theUser )
throws ChaiUnavailableException, ChaiOperationException
{
final Map values = theUser.readStringAttributes( TRADITIONAL_PASSWORD_ATTRIBUTES );
final int minLength = convertStrToInt( values.get( "passwordMinimumLength" ), 0 );
final int expireInterval = convertStrToInt( values.get( "passwordExpirationInterval" ), 0 );
final boolean uniqueRequired = convertStrToBoolean( values.get( "passwordUniqueRequired" ) );
final Map policyMap = new LinkedHashMap<>();
// default for legacy passwords;
policyMap.put( ChaiPasswordRule.MaximumLength, String.valueOf( 16 ) );
policyMap.put( ChaiPasswordRule.MinimumLength, String.valueOf( minLength ) );
policyMap.put( ChaiPasswordRule.ExpirationInterval, String.valueOf( expireInterval ) );
policyMap.put( ChaiPasswordRule.UniqueRequired, String.valueOf( uniqueRequired ) );
//other defaults for non up-policy.
policyMap.put( ChaiPasswordRule.AllowNumeric, String.valueOf( true ) );
policyMap.put( ChaiPasswordRule.AllowSpecial, String.valueOf( true ) );
policyMap.put( ChaiPasswordRule.CaseSensitive, String.valueOf( false ) );
return DefaultChaiPasswordPolicy.createDefaultChaiPasswordPolicyByRule( policyMap );
}
}
static String readGuid( final ChaiEntry entry )
throws ChaiUnavailableException, ChaiOperationException
{
final byte[] st = entry.getChaiProvider().readMultiByteAttribute( entry.getEntryDN(), "guid" )[0];
final BigInteger bigInt = new BigInteger( 1, st );
return bigInt.toString( 16 );
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy