diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/exception/CatalogExceptionMessage.java b/openmetadata-service/src/main/java/org/openmetadata/service/exception/CatalogExceptionMessage.java index 7935488bd8f..223f2a51896 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/exception/CatalogExceptionMessage.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/exception/CatalogExceptionMessage.java @@ -35,7 +35,7 @@ public final class CatalogExceptionMessage { public static final String PASSWORD_INVALID_FORMAT = "Password must be of minimum 8 characters, with one special, one Upper, one lower case character, and one Digit."; public static final String MAX_FAILED_LOGIN_ATTEMPT = - "Failed Login Attempts Exceeded. Please try after some time."; + "Failed Login Attempts Exceeded. Use Forgot Password or retry after some time."; public static final String INCORRECT_OLD_PASSWORD = "INCORRECT_OLD_PASSWORD"; diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java index 302882eb0c7..01b7d76d12f 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SystemRepository.java @@ -43,6 +43,7 @@ import org.openmetadata.service.search.SearchRepository; import org.openmetadata.service.secrets.SecretsManager; import org.openmetadata.service.secrets.SecretsManagerFactory; import org.openmetadata.service.security.JwtFilter; +import org.openmetadata.service.security.auth.LoginAttemptCache; import org.openmetadata.service.util.JsonUtils; import org.openmetadata.service.util.OpenMetadataConnectionBuilder; import org.openmetadata.service.util.RestUtil; @@ -249,6 +250,10 @@ public class SystemRepository { WorkflowHandler workflowHandler = WorkflowHandler.getInstance(); workflowHandler.initializeNewProcessEngine(workflowHandler.getProcessEngineConfiguration()); } + + if (settingsType == SettingsType.LOGIN_CONFIGURATION) { + LoginAttemptCache.updateLoginConfiguration(); + } } public void updateSetting(Settings setting) { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java index 16532c563ce..e68c3b891ae 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java @@ -118,7 +118,7 @@ public class SettingsCache { .withConfigValue( new LoginConfiguration() .withMaxLoginFailAttempts(3) - .withAccessBlockTime(600) + .withAccessBlockTime(30) .withJwtTokenExpiryTime(3600)); systemRepository.createNewSetting(setting); } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/BasicAuthenticator.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/BasicAuthenticator.java index a99a7eeab87..9d33d87927d 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/BasicAuthenticator.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/BasicAuthenticator.java @@ -102,7 +102,6 @@ public class BasicAuthenticator implements AuthenticatorHandler { private static final int HASHING_COST = 12; private UserRepository userRepository; private TokenRepository tokenRepository; - private LoginAttemptCache loginAttemptCache; private AuthorizerConfiguration authorizerConfiguration; private boolean isSelfSignUpAvailable; @@ -111,7 +110,6 @@ public class BasicAuthenticator implements AuthenticatorHandler { this.userRepository = (UserRepository) Entity.getEntityRepository(Entity.USER); this.tokenRepository = Entity.getTokenRepository(); this.authorizerConfiguration = config.getAuthorizerConfiguration(); - this.loginAttemptCache = new LoginAttemptCache(); this.isSelfSignUpAvailable = config.getAuthenticationConfiguration().getEnableSelfSignup(); } @@ -267,7 +265,7 @@ public class BasicAuthenticator implements AuthenticatorHandler { LOG.error("Error in sending Password Change Mail to User. Reason : " + ex.getMessage(), ex); throw new CustomExceptionMessage(424, FAILED_SEND_EMAIL, EMAIL_SENDING_ISSUE); } - loginAttemptCache.recordSuccessfulLogin(request.getUsername()); + LoginAttemptCache.getInstance().recordSuccessfulLogin(request.getUsername()); } @Override @@ -312,7 +310,7 @@ public class BasicAuthenticator implements AuthenticatorHandler { storedUser.getAuthenticationMechanism().setConfig(storedBasicAuthMechanism); PutResponse response = userRepository.createOrUpdate(uriInfo, storedUser); // remove login/details from cache - loginAttemptCache.recordSuccessfulLogin(userName); + LoginAttemptCache.getInstance().recordSuccessfulLogin(userName); // in case admin updates , send email to user if (request.getRequestType() == USER && getSmtpSettings().getEnableSmtpServer()) { @@ -476,7 +474,7 @@ public class BasicAuthenticator implements AuthenticatorHandler { @Override public void checkIfLoginBlocked(String email) { - if (loginAttemptCache.isLoginBlocked(email)) { + if (LoginAttemptCache.getInstance().isLoginBlocked(email)) { throw new AuthenticationException(MAX_FAILED_LOGIN_ATTEMPT); } } @@ -484,15 +482,15 @@ public class BasicAuthenticator implements AuthenticatorHandler { @Override public void recordFailedLoginAttempt(String email, String userName) throws TemplateException, IOException { - loginAttemptCache.recordFailedLogin(email); - int failedLoginAttempt = loginAttemptCache.getUserFailedLoginCount(email); + LoginAttemptCache.getInstance().recordFailedLogin(email); + int failedLoginAttempt = LoginAttemptCache.getInstance().getUserFailedLoginCount(email); if (failedLoginAttempt == SecurityUtil.getLoginConfiguration().getMaxLoginFailAttempts()) { sendAccountStatus( userName, email, "Multiple Failed Login Attempts.", String.format( - "Someone is trying to access your account. Login is Blocked for %s minutes. Please change your password.", + "Someone is trying to access your account. Login is Blocked for %s seconds. Please change your password.", SecurityUtil.getLoginConfiguration().getAccessBlockTime())); } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LdapAuthenticator.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LdapAuthenticator.java index 9531d76c1f9..d608434d7b2 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LdapAuthenticator.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LdapAuthenticator.java @@ -84,7 +84,6 @@ public class LdapAuthenticator implements AuthenticatorHandler { private RoleRepository roleRepository; private UserRepository userRepository; private TokenRepository tokenRepository; - private LoginAttemptCache loginAttemptCache; private LdapConfiguration ldapConfiguration; private LDAPConnectionPool ldapLookupConnectionPool; private boolean isSelfSignUpEnabled; @@ -102,7 +101,6 @@ public class LdapAuthenticator implements AuthenticatorHandler { this.roleRepository = (RoleRepository) Entity.getEntityRepository(Entity.ROLE); this.tokenRepository = Entity.getTokenRepository(); this.ldapConfiguration = config.getAuthenticationConfiguration().getLdapConfiguration(); - this.loginAttemptCache = new LoginAttemptCache(); this.isSelfSignUpEnabled = config.getAuthenticationConfiguration().getEnableSelfSignup(); } @@ -176,7 +174,7 @@ public class LdapAuthenticator implements AuthenticatorHandler { @Override public void checkIfLoginBlocked(String email) { - if (loginAttemptCache.isLoginBlocked(email)) { + if (LoginAttemptCache.getInstance().isLoginBlocked(email)) { throw new AuthenticationException(MAX_FAILED_LOGIN_ATTEMPT); } } @@ -184,8 +182,8 @@ public class LdapAuthenticator implements AuthenticatorHandler { @Override public void recordFailedLoginAttempt(String email, String userName) throws TemplateException, IOException { - loginAttemptCache.recordFailedLogin(email); - int failedLoginAttempt = loginAttemptCache.getUserFailedLoginCount(email); + LoginAttemptCache.getInstance().recordFailedLogin(email); + int failedLoginAttempt = LoginAttemptCache.getInstance().getUserFailedLoginCount(email); if (failedLoginAttempt == SecurityUtil.getLoginConfiguration().getMaxLoginFailAttempts()) { EmailUtil.sendAccountStatus( userName, diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LoginAttemptCache.java b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LoginAttemptCache.java index 0ceb672d098..336b97e473d 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LoginAttemptCache.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/security/auth/LoginAttemptCache.java @@ -3,6 +3,7 @@ package org.openmetadata.service.security.auth; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import io.dropwizard.logback.shaded.guava.annotations.VisibleForTesting; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import lombok.NonNull; @@ -12,10 +13,11 @@ import org.openmetadata.schema.settings.SettingsType; import org.openmetadata.service.resources.settings.SettingsCache; public class LoginAttemptCache { + private static LoginAttemptCache INSTANCE; private int maxAttempt = 3; private final LoadingCache attemptsCache; - public LoginAttemptCache() { + private LoginAttemptCache() { LoginConfiguration loginConfiguration = SettingsCache.getSetting(SettingsType.LOGIN_CONFIGURATION, LoginConfiguration.class); long accessBlockTime = 600; @@ -35,6 +37,18 @@ public class LoginAttemptCache { }); } + public static LoginAttemptCache getInstance() { + if (INSTANCE == null) { + INSTANCE = new LoginAttemptCache(); + } + return INSTANCE; + } + + public static void updateLoginConfiguration() { + INSTANCE = new LoginAttemptCache(); + } + + @VisibleForTesting public LoginAttemptCache(int maxAttempt, int blockTimeInSec) { this.maxAttempt = maxAttempt; attemptsCache = diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/ConfigResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/ConfigResourceTest.java index 9cf314ed175..9a80814133e 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/ConfigResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/ConfigResourceTest.java @@ -124,7 +124,7 @@ class ConfigResourceTest extends OpenMetadataApplicationTest { LoginConfiguration loginConfiguration = TestUtils.get(target, LoginConfiguration.class, TEST_AUTH_HEADERS); assertEquals(3, loginConfiguration.getMaxLoginFailAttempts()); - assertEquals(600, loginConfiguration.getAccessBlockTime()); + assertEquals(30, loginConfiguration.getAccessBlockTime()); assertEquals(3600, loginConfiguration.getJwtTokenExpiryTime()); } diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/SystemResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/SystemResourceTest.java index 1cf64c31e5e..686de7c1bd3 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/SystemResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/system/SystemResourceTest.java @@ -436,7 +436,7 @@ public class SystemResourceTest extends OpenMetadataApplicationTest { // Assert default values assertEquals(3, loginConfig.getMaxLoginFailAttempts()); - assertEquals(600, loginConfig.getAccessBlockTime()); + assertEquals(30, loginConfig.getAccessBlockTime()); assertEquals(3600, loginConfig.getJwtTokenExpiryTime()); // Update login configuration