Fix Login Configuration Issue (#19109)

* Fix Login Configuration Issue

* Fix Tests
This commit is contained in:
Mohit Yadav 2024-12-17 23:43:07 +05:30 committed by GitHub
parent c96c7785df
commit 4b9948dbfb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 33 additions and 18 deletions

View File

@ -35,7 +35,7 @@ public final class CatalogExceptionMessage {
public static final String PASSWORD_INVALID_FORMAT = 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."; "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 = 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"; public static final String INCORRECT_OLD_PASSWORD = "INCORRECT_OLD_PASSWORD";

View File

@ -43,6 +43,7 @@ import org.openmetadata.service.search.SearchRepository;
import org.openmetadata.service.secrets.SecretsManager; import org.openmetadata.service.secrets.SecretsManager;
import org.openmetadata.service.secrets.SecretsManagerFactory; import org.openmetadata.service.secrets.SecretsManagerFactory;
import org.openmetadata.service.security.JwtFilter; import org.openmetadata.service.security.JwtFilter;
import org.openmetadata.service.security.auth.LoginAttemptCache;
import org.openmetadata.service.util.JsonUtils; import org.openmetadata.service.util.JsonUtils;
import org.openmetadata.service.util.OpenMetadataConnectionBuilder; import org.openmetadata.service.util.OpenMetadataConnectionBuilder;
import org.openmetadata.service.util.RestUtil; import org.openmetadata.service.util.RestUtil;
@ -249,6 +250,10 @@ public class SystemRepository {
WorkflowHandler workflowHandler = WorkflowHandler.getInstance(); WorkflowHandler workflowHandler = WorkflowHandler.getInstance();
workflowHandler.initializeNewProcessEngine(workflowHandler.getProcessEngineConfiguration()); workflowHandler.initializeNewProcessEngine(workflowHandler.getProcessEngineConfiguration());
} }
if (settingsType == SettingsType.LOGIN_CONFIGURATION) {
LoginAttemptCache.updateLoginConfiguration();
}
} }
public void updateSetting(Settings setting) { public void updateSetting(Settings setting) {

View File

@ -118,7 +118,7 @@ public class SettingsCache {
.withConfigValue( .withConfigValue(
new LoginConfiguration() new LoginConfiguration()
.withMaxLoginFailAttempts(3) .withMaxLoginFailAttempts(3)
.withAccessBlockTime(600) .withAccessBlockTime(30)
.withJwtTokenExpiryTime(3600)); .withJwtTokenExpiryTime(3600));
systemRepository.createNewSetting(setting); systemRepository.createNewSetting(setting);
} }

View File

@ -102,7 +102,6 @@ public class BasicAuthenticator implements AuthenticatorHandler {
private static final int HASHING_COST = 12; private static final int HASHING_COST = 12;
private UserRepository userRepository; private UserRepository userRepository;
private TokenRepository tokenRepository; private TokenRepository tokenRepository;
private LoginAttemptCache loginAttemptCache;
private AuthorizerConfiguration authorizerConfiguration; private AuthorizerConfiguration authorizerConfiguration;
private boolean isSelfSignUpAvailable; private boolean isSelfSignUpAvailable;
@ -111,7 +110,6 @@ public class BasicAuthenticator implements AuthenticatorHandler {
this.userRepository = (UserRepository) Entity.getEntityRepository(Entity.USER); this.userRepository = (UserRepository) Entity.getEntityRepository(Entity.USER);
this.tokenRepository = Entity.getTokenRepository(); this.tokenRepository = Entity.getTokenRepository();
this.authorizerConfiguration = config.getAuthorizerConfiguration(); this.authorizerConfiguration = config.getAuthorizerConfiguration();
this.loginAttemptCache = new LoginAttemptCache();
this.isSelfSignUpAvailable = config.getAuthenticationConfiguration().getEnableSelfSignup(); 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); LOG.error("Error in sending Password Change Mail to User. Reason : " + ex.getMessage(), ex);
throw new CustomExceptionMessage(424, FAILED_SEND_EMAIL, EMAIL_SENDING_ISSUE); throw new CustomExceptionMessage(424, FAILED_SEND_EMAIL, EMAIL_SENDING_ISSUE);
} }
loginAttemptCache.recordSuccessfulLogin(request.getUsername()); LoginAttemptCache.getInstance().recordSuccessfulLogin(request.getUsername());
} }
@Override @Override
@ -312,7 +310,7 @@ public class BasicAuthenticator implements AuthenticatorHandler {
storedUser.getAuthenticationMechanism().setConfig(storedBasicAuthMechanism); storedUser.getAuthenticationMechanism().setConfig(storedBasicAuthMechanism);
PutResponse<User> response = userRepository.createOrUpdate(uriInfo, storedUser); PutResponse<User> response = userRepository.createOrUpdate(uriInfo, storedUser);
// remove login/details from cache // remove login/details from cache
loginAttemptCache.recordSuccessfulLogin(userName); LoginAttemptCache.getInstance().recordSuccessfulLogin(userName);
// in case admin updates , send email to user // in case admin updates , send email to user
if (request.getRequestType() == USER && getSmtpSettings().getEnableSmtpServer()) { if (request.getRequestType() == USER && getSmtpSettings().getEnableSmtpServer()) {
@ -476,7 +474,7 @@ public class BasicAuthenticator implements AuthenticatorHandler {
@Override @Override
public void checkIfLoginBlocked(String email) { public void checkIfLoginBlocked(String email) {
if (loginAttemptCache.isLoginBlocked(email)) { if (LoginAttemptCache.getInstance().isLoginBlocked(email)) {
throw new AuthenticationException(MAX_FAILED_LOGIN_ATTEMPT); throw new AuthenticationException(MAX_FAILED_LOGIN_ATTEMPT);
} }
} }
@ -484,15 +482,15 @@ public class BasicAuthenticator implements AuthenticatorHandler {
@Override @Override
public void recordFailedLoginAttempt(String email, String userName) public void recordFailedLoginAttempt(String email, String userName)
throws TemplateException, IOException { throws TemplateException, IOException {
loginAttemptCache.recordFailedLogin(email); LoginAttemptCache.getInstance().recordFailedLogin(email);
int failedLoginAttempt = loginAttemptCache.getUserFailedLoginCount(email); int failedLoginAttempt = LoginAttemptCache.getInstance().getUserFailedLoginCount(email);
if (failedLoginAttempt == SecurityUtil.getLoginConfiguration().getMaxLoginFailAttempts()) { if (failedLoginAttempt == SecurityUtil.getLoginConfiguration().getMaxLoginFailAttempts()) {
sendAccountStatus( sendAccountStatus(
userName, userName,
email, email,
"Multiple Failed Login Attempts.", "Multiple Failed Login Attempts.",
String.format( 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())); SecurityUtil.getLoginConfiguration().getAccessBlockTime()));
} }
} }

View File

@ -84,7 +84,6 @@ public class LdapAuthenticator implements AuthenticatorHandler {
private RoleRepository roleRepository; private RoleRepository roleRepository;
private UserRepository userRepository; private UserRepository userRepository;
private TokenRepository tokenRepository; private TokenRepository tokenRepository;
private LoginAttemptCache loginAttemptCache;
private LdapConfiguration ldapConfiguration; private LdapConfiguration ldapConfiguration;
private LDAPConnectionPool ldapLookupConnectionPool; private LDAPConnectionPool ldapLookupConnectionPool;
private boolean isSelfSignUpEnabled; private boolean isSelfSignUpEnabled;
@ -102,7 +101,6 @@ public class LdapAuthenticator implements AuthenticatorHandler {
this.roleRepository = (RoleRepository) Entity.getEntityRepository(Entity.ROLE); this.roleRepository = (RoleRepository) Entity.getEntityRepository(Entity.ROLE);
this.tokenRepository = Entity.getTokenRepository(); this.tokenRepository = Entity.getTokenRepository();
this.ldapConfiguration = config.getAuthenticationConfiguration().getLdapConfiguration(); this.ldapConfiguration = config.getAuthenticationConfiguration().getLdapConfiguration();
this.loginAttemptCache = new LoginAttemptCache();
this.isSelfSignUpEnabled = config.getAuthenticationConfiguration().getEnableSelfSignup(); this.isSelfSignUpEnabled = config.getAuthenticationConfiguration().getEnableSelfSignup();
} }
@ -176,7 +174,7 @@ public class LdapAuthenticator implements AuthenticatorHandler {
@Override @Override
public void checkIfLoginBlocked(String email) { public void checkIfLoginBlocked(String email) {
if (loginAttemptCache.isLoginBlocked(email)) { if (LoginAttemptCache.getInstance().isLoginBlocked(email)) {
throw new AuthenticationException(MAX_FAILED_LOGIN_ATTEMPT); throw new AuthenticationException(MAX_FAILED_LOGIN_ATTEMPT);
} }
} }
@ -184,8 +182,8 @@ public class LdapAuthenticator implements AuthenticatorHandler {
@Override @Override
public void recordFailedLoginAttempt(String email, String userName) public void recordFailedLoginAttempt(String email, String userName)
throws TemplateException, IOException { throws TemplateException, IOException {
loginAttemptCache.recordFailedLogin(email); LoginAttemptCache.getInstance().recordFailedLogin(email);
int failedLoginAttempt = loginAttemptCache.getUserFailedLoginCount(email); int failedLoginAttempt = LoginAttemptCache.getInstance().getUserFailedLoginCount(email);
if (failedLoginAttempt == SecurityUtil.getLoginConfiguration().getMaxLoginFailAttempts()) { if (failedLoginAttempt == SecurityUtil.getLoginConfiguration().getMaxLoginFailAttempts()) {
EmailUtil.sendAccountStatus( EmailUtil.sendAccountStatus(
userName, userName,

View File

@ -3,6 +3,7 @@ package org.openmetadata.service.security.auth;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import io.dropwizard.logback.shaded.guava.annotations.VisibleForTesting;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import lombok.NonNull; import lombok.NonNull;
@ -12,10 +13,11 @@ import org.openmetadata.schema.settings.SettingsType;
import org.openmetadata.service.resources.settings.SettingsCache; import org.openmetadata.service.resources.settings.SettingsCache;
public class LoginAttemptCache { public class LoginAttemptCache {
private static LoginAttemptCache INSTANCE;
private int maxAttempt = 3; private int maxAttempt = 3;
private final LoadingCache<String, Integer> attemptsCache; private final LoadingCache<String, Integer> attemptsCache;
public LoginAttemptCache() { private LoginAttemptCache() {
LoginConfiguration loginConfiguration = LoginConfiguration loginConfiguration =
SettingsCache.getSetting(SettingsType.LOGIN_CONFIGURATION, LoginConfiguration.class); SettingsCache.getSetting(SettingsType.LOGIN_CONFIGURATION, LoginConfiguration.class);
long accessBlockTime = 600; 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) { public LoginAttemptCache(int maxAttempt, int blockTimeInSec) {
this.maxAttempt = maxAttempt; this.maxAttempt = maxAttempt;
attemptsCache = attemptsCache =

View File

@ -124,7 +124,7 @@ class ConfigResourceTest extends OpenMetadataApplicationTest {
LoginConfiguration loginConfiguration = LoginConfiguration loginConfiguration =
TestUtils.get(target, LoginConfiguration.class, TEST_AUTH_HEADERS); TestUtils.get(target, LoginConfiguration.class, TEST_AUTH_HEADERS);
assertEquals(3, loginConfiguration.getMaxLoginFailAttempts()); assertEquals(3, loginConfiguration.getMaxLoginFailAttempts());
assertEquals(600, loginConfiguration.getAccessBlockTime()); assertEquals(30, loginConfiguration.getAccessBlockTime());
assertEquals(3600, loginConfiguration.getJwtTokenExpiryTime()); assertEquals(3600, loginConfiguration.getJwtTokenExpiryTime());
} }

View File

@ -436,7 +436,7 @@ public class SystemResourceTest extends OpenMetadataApplicationTest {
// Assert default values // Assert default values
assertEquals(3, loginConfig.getMaxLoginFailAttempts()); assertEquals(3, loginConfig.getMaxLoginFailAttempts());
assertEquals(600, loginConfig.getAccessBlockTime()); assertEquals(30, loginConfig.getAccessBlockTime());
assertEquals(3600, loginConfig.getJwtTokenExpiryTime()); assertEquals(3600, loginConfig.getJwtTokenExpiryTime());
// Update login configuration // Update login configuration