diff --git a/README.md b/README.md index 50907c58..e0875967 100644 --- a/README.md +++ b/README.md @@ -237,7 +237,7 @@ The Current list of settings is security: enableLogin: false # set to 'true' to enable login csrfDisabled: true # Set to 'true' to disable CSRF protection (not recommended for production) - loginAttemptCount: 5 # lock user account after 5 tries + loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1 loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts loginMethod: all # 'all' (Login Username/Password and OAuth2[must be enabled and configured]), 'normal'(only Login with Username/Password) or 'oauth2'(only Login with OAuth2) initialLogin: diff --git a/src/main/java/stirling/software/SPDF/config/security/LoginAttemptService.java b/src/main/java/stirling/software/SPDF/config/security/LoginAttemptService.java index 6141be29..42886289 100644 --- a/src/main/java/stirling/software/SPDF/config/security/LoginAttemptService.java +++ b/src/main/java/stirling/software/SPDF/config/security/LoginAttemptService.java @@ -7,21 +7,28 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.AttemptCounter; @Service +@Slf4j public class LoginAttemptService { - @Autowired ApplicationProperties applicationProperties; + @Autowired private ApplicationProperties applicationProperties; private int MAX_ATTEMPT; private long ATTEMPT_INCREMENT_TIME; private ConcurrentHashMap attemptsCache; + private boolean isBlockedEnabled = true; @PostConstruct public void init() { MAX_ATTEMPT = applicationProperties.getSecurity().getLoginAttemptCount(); + if (MAX_ATTEMPT == -1) { + isBlockedEnabled = false; + log.info("Login attempt tracking is disabled."); + } ATTEMPT_INCREMENT_TIME = TimeUnit.MINUTES.toMillis( applicationProperties.getSecurity().getLoginResetTimeMinutes()); @@ -29,14 +36,16 @@ public class LoginAttemptService { } public void loginSucceeded(String key) { - if (key == null || key.trim().isEmpty()) { + if (!isBlockedEnabled || key == null || key.trim().isEmpty()) { return; } attemptsCache.remove(key.toLowerCase()); } public void loginFailed(String key) { - if (key == null || key.trim().isEmpty()) return; + if (!isBlockedEnabled || key == null || key.trim().isEmpty()) { + return; + } AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase()); if (attemptCounter == null) { @@ -51,7 +60,9 @@ public class LoginAttemptService { } public boolean isBlocked(String key) { - if (key == null || key.trim().isEmpty()) return false; + if (!isBlockedEnabled || key == null || key.trim().isEmpty()) { + return false; + } AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase()); if (attemptCounter == null) { return false; @@ -61,7 +72,9 @@ public class LoginAttemptService { } public int getRemainingAttempts(String key) { - if (key == null || key.trim().isEmpty()) return MAX_ATTEMPT; + if (!isBlockedEnabled || key == null || key.trim().isEmpty()) { + return Integer.MAX_VALUE; // Arbitrarily high number if tracking is disabled + } AttemptCounter attemptCounter = attemptsCache.get(key.toLowerCase()); if (attemptCounter == null) { diff --git a/src/main/resources/settings.yml.template b/src/main/resources/settings.yml.template index d139e712..2e06ea9f 100644 --- a/src/main/resources/settings.yml.template +++ b/src/main/resources/settings.yml.template @@ -14,7 +14,7 @@ security: enableLogin: false # set to 'true' to enable login csrfDisabled: true # Set to 'true' to disable CSRF protection (not recommended for production) - loginAttemptCount: 5 # lock user account after 5 tries + loginAttemptCount: 5 # lock user account after 5 tries; when using e.g. Fail2Ban you can deactivate the function with -1 loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts loginMethod: all # 'all' (Login Username/Password and OAuth2[must be enabled and configured]), 'normal'(only Login with Username/Password) or 'oauth2'(only Login with OAuth2) initialLogin: