package org.xdi.service.security.protect;

import java.util.List;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.python.jline.internal.Log;
import org.xdi.model.security.protect.AuthenticationAttempt;
import org.xdi.model.security.protect.AuthenticationAttemptList;
import org.xdi.service.CacheService;

/**
 * Base Brute Force authentication protection service implementation
 *
 * @author Yuriy Movchan Date: 08/21/2018
 */
public abstract class AuthenticationProtectionService {

    @Inject
    private CacheService cacheService;

    protected int attemptExpiration;
    protected int maximumAllowedAttemptsWithoutDelay;

    protected int delayTime;

    @PostConstruct
    public void create() {
        init();
    }

    protected abstract void init();

    public void storeAttempt(String key, boolean success) {
        AuthenticationAttemptList authenticationAttemptList = getNonExpiredAttempts(key);
        if (authenticationAttemptList == null) {
            authenticationAttemptList = new AuthenticationAttemptList();
        }

        long currentTime = System.currentTimeMillis();

        AuthenticationAttempt authenticationAttempt = new AuthenticationAttempt(currentTime, currentTime + attemptExpiration * 1000, success);
        authenticationAttemptList.getAuthenticationAttempts().add(authenticationAttempt);

        cacheService.put(Integer.toString(attemptExpiration), buildKey(key), authenticationAttemptList);
    }

    public AuthenticationAttemptList getAttempts(String key) {
        Object o = cacheService.get(null, buildKey(key));
        if (o instanceof AuthenticationAttemptList) {
            return (AuthenticationAttemptList) o;
        }

        return null;
    }

    public AuthenticationAttemptList getNonExpiredAttempts(String key) {
        AuthenticationAttemptList authenticationAttemptList = getAttempts(key);
        if (authenticationAttemptList == null) {
            return null;
        }

        long currentTime = System.currentTimeMillis();

        AuthenticationAttemptList result = new AuthenticationAttemptList();
        List<AuthenticationAttempt> resultAuthenticationAttemptList = result.getAuthenticationAttempts();
        for (AuthenticationAttempt authenticationAttempt : authenticationAttemptList.getAuthenticationAttempts()) {
            if (authenticationAttempt.getExpiration() > currentTime) {
                resultAuthenticationAttemptList.add(authenticationAttempt);
            }
        }

        return result;

    }

    public boolean isReachAttemptRateLimit(String key) {
        AuthenticationAttemptList authenticationAttemptList = getNonExpiredAttempts(key);
        if (authenticationAttemptList == null) {
            return false;
        }

        return authenticationAttemptList.getAuthenticationAttempts().size() >= maximumAllowedAttemptsWithoutDelay;
    }

    public int getDelayTime() {
        return delayTime;
    }
    
    public void doDelayIfNeeded(String key) {
        boolean processDelay = isReachAttemptRateLimit(key);
        if (!processDelay) {
            return;
        }
        
        int delayTime = getDelayTime();
        
        try {
            Thread.sleep(delayTime * 1000);
        } catch (InterruptedException ex) {
            Log.error("Failed to process authentication delay");
        }
    }

    private String buildKey(String key) {
        return getKeyPrefix() + "_" + key.toLowerCase();
    }

    protected abstract String getKeyPrefix();

}

@@ -166,16 +166,41 @@
 al.Log;%0A
+import org.slf4j.Logger;%0A
 import o
@@ -582,24 +582,61 @@
 heService;%0A%0A
+    @Inject%0A    private Logger log;%0A%0A
     protecte
@@ -3151,24 +3151,95 @@
 essDelay) %7B%0A
+            log.debug(%22Allowing current login attempt without delay%22);%0A
             
@@ -3328,16 +3328,104 @@
   try %7B%0A
+            log.debug(%22Current login attempt requires delay: '%7B%7D' seconds%22, delayTime);%0A
         

Add debug to AuthenticationProtectionService