in interceptors/authn/src/main/java/org/apache/directory/server/core/authn/AuthenticationInterceptor.java [531:828]
public void bind( BindOperationContext bindContext ) throws LdapException
{
if ( IS_DEBUG )
{
LOG.debug( "Operation Context: {}", bindContext );
}
CoreSession session = bindContext.getSession();
Dn bindDn = bindContext.getDn();
if ( ( session != null )
&& ( session.getEffectivePrincipal() != null )
&& ( !session.isAnonymous() )
&& ( !session.isAdministrator() ) )
{
// null out the credentials
bindContext.setCredentials( null );
}
// pick the first matching authenticator type
AuthenticationLevel level = bindContext.getAuthenticationLevel();
if ( level == AuthenticationLevel.UNAUTHENT )
{
// This is a case where the Bind request contains a Dn, but no password.
// We don't check the Dn, we just return a UnwillingToPerform error
// Cf RFC 4513, chap. 5.1.2
throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, "Cannot Bind for Dn "
+ bindDn.getName() );
}
PasswordPolicyException ppe = null;
boolean isPPolicyReqCtrlPresent = bindContext.hasRequestControl( PasswordPolicyRequest.OID );
PasswordPolicyResponse pwdRespCtrl = new PasswordPolicyResponseImpl();
boolean authenticated = false;
Authenticator authenticator = selectAuthenticator( bindDn, level );
try
{
// perform the authentication
LdapPrincipal principal = authenticator.authenticate( bindContext );
if ( principal != null )
{
LdapPrincipal clonedPrincipal = ( LdapPrincipal ) ( principal.clone() );
// remove creds so there is no security risk
bindContext.setCredentials( null );
clonedPrincipal.setUserPassword( Strings.EMPTY_BYTES );
// authentication was successful
CoreSession newSession = new DefaultCoreSession( clonedPrincipal, directoryService );
bindContext.setSession( newSession );
authenticated = true;
}
}
catch ( PasswordPolicyException e )
{
ppe = e;
}
catch ( LdapAuthenticationException e )
{
// authentication failed, try the next authenticator
LOG.info( "Authenticator {} failed to authenticate: {}", authenticator, bindContext.getDn() );
}
catch ( Exception e )
{
// Log other exceptions than LdapAuthenticationException
LOG.info( "Unexpected failure for Authenticator {} : {}", authenticator, bindContext.getDn() );
}
if ( ppe != null )
{
if ( isPPolicyReqCtrlPresent )
{
pwdRespCtrl.setPasswordPolicyError( PasswordPolicyErrorEnum.get( ppe.getErrorCode() ) );
bindContext.addResponseControl( pwdRespCtrl );
}
throw ppe;
}
Entry userEntry = bindContext.getEntry();
PasswordPolicyConfiguration policyConfig = getPwdPolicy( userEntry );
// load the user entry again if ppolicy is enabled, cause the authenticator might have modified the entry
if ( policyConfig != null )
{
LookupOperationContext lookupContext = new LookupOperationContext( adminSession, bindDn,
SchemaConstants.ALL_ATTRIBUTES_ARRAY );
lookupContext.setPartition( bindContext.getPartition() );
lookupContext.setTransaction( bindContext.getTransaction() );
userEntry = directoryService.getPartitionNexus().lookup( lookupContext );
}
// check if the user entry is null, it will be null
// in cases of anonymous bind
if ( authenticated && ( userEntry == null ) && directoryService.isAllowAnonymousAccess() )
{
return;
}
if ( !authenticated )
{
if ( LOG.isInfoEnabled() )
{
LOG.info( "Cannot bind to the server " );
}
if ( ( policyConfig != null ) && ( userEntry != null ) )
{
Attribute pwdFailTimeAt = userEntry.get( pwdFailurTimeAT );
if ( pwdFailTimeAt == null )
{
pwdFailTimeAt = new DefaultAttribute( pwdFailurTimeAT );
}
else
{
purgeFailureTimes( policyConfig, pwdFailTimeAt );
}
String failureTime = DateUtils.getGeneralizedTime( directoryService.getTimeProvider() );
pwdFailTimeAt.add( failureTime );
Modification pwdFailTimeMod = new DefaultModification( REPLACE_ATTRIBUTE, pwdFailTimeAt );
List<Modification> mods = new ArrayList<>();
mods.add( pwdFailTimeMod );
int numFailures = pwdFailTimeAt.size();
if ( policyConfig.isPwdLockout() && ( numFailures >= policyConfig.getPwdMaxFailure() ) )
{
// Checking that we're not locking the admin user of the system partition
// See DIRSERVER-1812 (The default admin account should never get locked forever)
if ( !userEntry.getDn().equals( new Dn( schemaManager, ServerDNConstants.ADMIN_SYSTEM_DN ) ) )
{
Attribute pwdAccountLockedTimeAt = new DefaultAttribute( pwdAccountLockedTimeAT );
// if zero, lockout permanently, only admin can unlock it
if ( policyConfig.getPwdLockoutDuration() == 0 )
{
pwdAccountLockedTimeAt.add( "000001010000Z" );
}
else
{
pwdAccountLockedTimeAt.add( failureTime );
}
Modification pwdAccountLockedMod = new DefaultModification( REPLACE_ATTRIBUTE,
pwdAccountLockedTimeAt );
mods.add( pwdAccountLockedMod );
pwdRespCtrl.setPasswordPolicyError( PasswordPolicyErrorEnum.ACCOUNT_LOCKED );
}
}
else if ( policyConfig.getPwdMinDelay() > 0 )
{
int numDelay = numFailures * policyConfig.getPwdMinDelay();
int maxDelay = policyConfig.getPwdMaxDelay();
if ( numDelay > maxDelay )
{
numDelay = maxDelay;
}
try
{
Thread.sleep( numDelay * 1000L );
}
catch ( InterruptedException e )
{
LOG.warn(
"Interrupted while delaying to send the failed authentication response for the user {}",
bindDn, e );
}
}
if ( !mods.isEmpty() )
{
String csnVal = directoryService.getCSN().toString();
Modification csnMod = new DefaultModification( REPLACE_ATTRIBUTE, directoryService.getAtProvider()
.getEntryCSN(), csnVal );
mods.add( csnMod );
ModifyOperationContext bindModCtx = new ModifyOperationContext( adminSession );
bindModCtx.setDn( bindDn );
bindModCtx.setEntry( userEntry );
bindModCtx.setModItems( mods );
bindModCtx.setPushToEvtInterceptor( true );
internalModify( bindContext, bindModCtx );
}
}
String upDn = bindDn == null ? "" : bindDn.getName();
throw new LdapAuthenticationException( I18n.err( I18n.ERR_14003_CANNOT_AUTHENTICATE_USER, upDn ) );
}
else if ( policyConfig != null )
{
List<Modification> mods = new ArrayList<>();
if ( policyConfig.getPwdMaxIdle() > 0 )
{
Attribute pwdLastSuccesTimeAt = new DefaultAttribute( pwdLastSuccessAT );
pwdLastSuccesTimeAt.add( DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) );
Modification pwdLastSuccesTimeMod = new DefaultModification( REPLACE_ATTRIBUTE, pwdLastSuccesTimeAt );
mods.add( pwdLastSuccesTimeMod );
}
Attribute pwdFailTimeAt = userEntry.get( pwdFailurTimeAT );
if ( pwdFailTimeAt != null )
{
Modification pwdFailTimeMod = new DefaultModification( REMOVE_ATTRIBUTE, pwdFailTimeAt );
mods.add( pwdFailTimeMod );
}
Attribute pwdAccLockedTimeAt = userEntry.get( pwdAccountLockedTimeAT );
if ( pwdAccLockedTimeAt != null )
{
Modification pwdAccLockedTimeMod = new DefaultModification( REMOVE_ATTRIBUTE, pwdAccLockedTimeAt );
mods.add( pwdAccLockedTimeMod );
}
// checking the expiration time *after* performing authentication, do we need to care about millisecond precision?
if ( ( policyConfig.getPwdMaxAge() > 0 ) && ( policyConfig.getPwdGraceAuthNLimit() > 0 ) )
{
Attribute pwdChangeTimeAttr = userEntry.get( pwdChangedTimeAT );
if ( pwdChangeTimeAttr != null )
{
boolean expired = PasswordUtil.isPwdExpired( pwdChangeTimeAttr.getString(),
policyConfig.getPwdMaxAge(), directoryService.getTimeProvider() );
if ( expired )
{
Attribute pwdGraceUseAttr = userEntry.get( pwdGraceUseTimeAT );
int numGraceAuth;
if ( pwdGraceUseAttr != null )
{
numGraceAuth = policyConfig.getPwdGraceAuthNLimit() - ( pwdGraceUseAttr.size() + 1 );
}
else
{
pwdGraceUseAttr = new DefaultAttribute( pwdGraceUseTimeAT );
numGraceAuth = policyConfig.getPwdGraceAuthNLimit() - 1;
}
pwdRespCtrl.setGraceAuthNRemaining( numGraceAuth );
pwdGraceUseAttr.add( DateUtils.getGeneralizedTime( directoryService.getTimeProvider() ) );
Modification pwdGraceUseMod = new DefaultModification( ADD_ATTRIBUTE, pwdGraceUseAttr );
mods.add( pwdGraceUseMod );
}
}
}
if ( !mods.isEmpty() )
{
String csnVal = directoryService.getCSN().toString();
Modification csnMod = new DefaultModification( REPLACE_ATTRIBUTE, directoryService.getAtProvider()
.getEntryCSN(), csnVal );
mods.add( csnMod );
ModifyOperationContext bindModCtx = new ModifyOperationContext( adminSession );
bindModCtx.setDn( bindDn );
bindModCtx.setEntry( userEntry );
bindModCtx.setModItems( mods );
bindModCtx.setPushToEvtInterceptor( true );
internalModify( bindContext, bindModCtx );
}
if ( isPPolicyReqCtrlPresent )
{
int expiryWarnTime = getPwdTimeBeforeExpiry( userEntry, policyConfig );
if ( expiryWarnTime > 0 )
{
pwdRespCtrl.setTimeBeforeExpiration( expiryWarnTime );
}
if ( isPwdMustReset( userEntry ) )
{
pwdRespCtrl.setPasswordPolicyError( PasswordPolicyErrorEnum.CHANGE_AFTER_RESET );
bindContext.getSession().setPwdMustChange( true );
}
bindContext.addResponseControl( pwdRespCtrl );
}
}
}