package org.cyclop.service.security.intern; import java.net.InetAddress; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; import javax.inject.Inject; import javax.inject.Named; import org.cyclop.common.AppConfig; import org.cyclop.service.security.BruteForceService; import org.cyclop.validation.EnableValidation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Named @EnableValidation class BruteForceServiceImpl implements BruteForceService { private final static Logger LOG = LoggerFactory.getLogger(BruteForceServiceImpl.class); @Inject private AppConfig config; private AtomicLong blockTime = new AtomicLong(); private long startBlocking = 0; @Override public boolean checkActive(Optional<InetAddress> clientIp, Optional<InetAddress> proxyIp) { return blockTime.get() > 0; } @Override public synchronized void resetLoginFailed(Optional<InetAddress> clientIp, Optional<InetAddress> proxyIp) { LOG.debug("Reseting login delay"); startBlocking = 0; blockTime.set(0); } @Override public synchronized void loginFailed(Optional<String> errorMessage, Optional<InetAddress> clientIp, Optional<InetAddress> proxyIp) { if (config.login.blockDelayMs <= 0) { LOG.debug("Brute force protection is disabled"); return; } long blockMs = calculateBlockTime(); if (blockMs == 0) { return; } if (blockMs > 5000) { LOG.info("Incorrect login from client: {} with proxy {}, message: {} - blocking for {} ms", clientIp.orElse(null), proxyIp.orElse(null), errorMessage.orElse("-"), blockMs); } try { Thread.sleep(blockMs); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } private long calculateBlockTime() { if (startBlocking == 0 || ((System.currentTimeMillis() - startBlocking) >= config.login.blockDelayResetMs)) { startBlocking = System.currentTimeMillis(); blockTime.set(config.login.blockDelayMs); } else { blockTime.set(Math.min((int) (blockTime.get() * config.login.blockDelayMultiplikator), config.login.maxBlockMs)); startBlocking = System.currentTimeMillis(); } return blockTime.get(); } }