public void bind()

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 );
            }
        }
    }