Use cache for password hash while validating LDAP password (#15993)

This commit is contained in:
Kashif Faraz 2024-02-28 18:33:33 +05:30 committed by GitHub
parent d2c2036ea2
commit f757231420
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 28 additions and 33 deletions

View File

@ -92,9 +92,9 @@ public class LdapUserPrincipal implements Principal
return lastVerified.get(); return lastVerified.get();
} }
public boolean hasSameCredentials(char[] password) public boolean hasSameCredentials(char[] password, PasswordHashGenerator hashGenerator)
{ {
byte[] recalculatedHash = PasswordHashGenerator.computePasswordHash( byte[] recalculatedHash = hashGenerator.getOrComputePasswordHash(
password, password,
this.credentials.getSalt(), this.credentials.getSalt(),
this.credentials.getIterations() this.credentials.getIterations()

View File

@ -153,14 +153,13 @@ public class LDAPCredentialsValidator implements CredentialsValidator
char[] password char[] password
) )
{ {
SearchResult userResult; final SearchResult userResult;
LdapName userDn; final LdapName userDn;
Map<String, Object> contextMap = new HashMap<>(); final Map<String, Object> contextMap = new HashMap<>();
final LdapUserPrincipal principal = this.cache.getOrExpire(username);
LdapUserPrincipal principal = this.cache.getOrExpire(username); if (principal != null && principal.hasSameCredentials(password, hashGenerator)) {
if (principal != null && principal.hasSameCredentials(password)) {
contextMap.put(BasicAuthUtils.SEARCH_RESULT_CONTEXT_KEY, principal.getSearchResult()); contextMap.put(BasicAuthUtils.SEARCH_RESULT_CONTEXT_KEY, principal.getSearchResult());
return new AuthenticationResult(username, authorizerName, authenticatorName, contextMap);
} else { } else {
ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
try { try {
@ -208,8 +207,8 @@ public class LDAPCredentialsValidator implements CredentialsValidator
this.cache.put(username, newPrincipal); this.cache.put(username, newPrincipal);
contextMap.put(BasicAuthUtils.SEARCH_RESULT_CONTEXT_KEY, userResult); contextMap.put(BasicAuthUtils.SEARCH_RESULT_CONTEXT_KEY, userResult);
return new AuthenticationResult(username, authorizerName, authenticatorName, contextMap);
} }
return new AuthenticationResult(username, authorizerName, authenticatorName, contextMap);
} }
/** /**

View File

@ -36,7 +36,6 @@ import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult; import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext; import javax.naming.ldap.LdapContext;
import javax.naming.spi.InitialContextFactory; import javax.naming.spi.InitialContextFactory;
import java.util.Collections; import java.util.Collections;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Iterator; import java.util.Iterator;
@ -94,6 +93,7 @@ public class LDAPCredentialsValidatorTest
properties properties
); );
validator.validateCredentials("ldap", "ldap", "validUser", "password".toCharArray()); validator.validateCredentials("ldap", "ldap", "validUser", "password".toCharArray());
validator.validateCredentials("ldap", "ldap", "validUser", "password".toCharArray());
} }
public static class MockContextFactory implements InitialContextFactory public static class MockContextFactory implements InitialContextFactory

View File

@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
package org.apache.druid.security.authentication.validator; package org.apache.druid.security.basic.authentication.validator;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.inject.Provider; import com.google.inject.Provider;
@ -28,28 +28,21 @@ import org.apache.druid.security.basic.authentication.db.cache.BasicAuthenticato
import org.apache.druid.security.basic.authentication.entity.BasicAuthenticatorCredentialUpdate; import org.apache.druid.security.basic.authentication.entity.BasicAuthenticatorCredentialUpdate;
import org.apache.druid.security.basic.authentication.entity.BasicAuthenticatorCredentials; import org.apache.druid.security.basic.authentication.entity.BasicAuthenticatorCredentials;
import org.apache.druid.security.basic.authentication.entity.BasicAuthenticatorUser; import org.apache.druid.security.basic.authentication.entity.BasicAuthenticatorUser;
import org.apache.druid.security.basic.authentication.validator.MetadataStoreCredentialsValidator;
import org.apache.druid.server.security.Access; import org.apache.druid.server.security.Access;
import org.apache.druid.server.security.AuthenticationResult; import org.apache.druid.server.security.AuthenticationResult;
import org.easymock.EasyMock; import org.easymock.EasyMock;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.Map; import java.util.Map;
public class DBCredentialsValidatorTest public class MetadataStoreCredentialsValidatorTest
{ {
private static final BasicAuthenticatorCredentials USER_A_CREDENTIALS = new BasicAuthenticatorCredentials(
@Rule
public ExpectedException expectedException = ExpectedException.none();
private static BasicAuthenticatorCredentials USER_A_CREDENTIALS = new BasicAuthenticatorCredentials(
new BasicAuthenticatorCredentialUpdate("helloworld", 20) new BasicAuthenticatorCredentialUpdate("helloworld", 20)
); );
private static Provider<BasicAuthenticatorCacheManager> CACHE_MANAGER_PROVIDER = Providers.of( private static final Provider<BasicAuthenticatorCacheManager> CACHE_MANAGER_PROVIDER = Providers.of(
new BasicAuthenticatorCacheManager() new BasicAuthenticatorCacheManager()
{ {
@Override @Override
@ -69,9 +62,8 @@ public class DBCredentialsValidatorTest
} }
); );
private static MetadataStoreCredentialsValidator validator = new MetadataStoreCredentialsValidator(CACHE_MANAGER_PROVIDER); private static final MetadataStoreCredentialsValidator VALIDATOR
= new MetadataStoreCredentialsValidator(CACHE_MANAGER_PROVIDER);
@Test @Test
public void validateBadAuthenticator() public void validateBadAuthenticator()
@ -87,9 +79,11 @@ public class DBCredentialsValidatorTest
MetadataStoreCredentialsValidator validator = new MetadataStoreCredentialsValidator(Providers.of(cacheManager)); MetadataStoreCredentialsValidator validator = new MetadataStoreCredentialsValidator(Providers.of(cacheManager));
expectedException.expect(IAE.class); IAE exception = Assert.assertThrows(
expectedException.expectMessage("No userMap is available for authenticator with prefix: [notbasic]"); IAE.class,
validator.validateCredentials(authenticatorName, authorizerName, username, password.toCharArray()); () -> validator.validateCredentials(authenticatorName, authorizerName, username, password.toCharArray())
);
Assert.assertEquals("No userMap is available for authenticator with prefix: [notbasic]", exception.getMessage());
EasyMock.verify(cacheManager); EasyMock.verify(cacheManager);
} }
@ -102,7 +96,7 @@ public class DBCredentialsValidatorTest
String username = "userB"; String username = "userB";
String password = "helloworld"; String password = "helloworld";
AuthenticationResult result = validator.validateCredentials(authenticatorName, authorizerName, username, password.toCharArray()); AuthenticationResult result = VALIDATOR.validateCredentials(authenticatorName, authorizerName, username, password.toCharArray());
Assert.assertNull(result); Assert.assertNull(result);
} }
@ -114,7 +108,7 @@ public class DBCredentialsValidatorTest
String username = "userC"; String username = "userC";
String password = "helloworld"; String password = "helloworld";
AuthenticationResult result = validator.validateCredentials(authenticatorName, authorizerName, username, password.toCharArray()); AuthenticationResult result = VALIDATOR.validateCredentials(authenticatorName, authorizerName, username, password.toCharArray());
Assert.assertNull(result); Assert.assertNull(result);
} }
@ -126,7 +120,7 @@ public class DBCredentialsValidatorTest
String username = "userA"; String username = "userA";
String password = "helloworld"; String password = "helloworld";
AuthenticationResult result = validator.validateCredentials(authenticatorName, authorizerName, username, password.toCharArray()); AuthenticationResult result = VALIDATOR.validateCredentials(authenticatorName, authorizerName, username, password.toCharArray());
Assert.assertNotNull(result); Assert.assertNotNull(result);
Assert.assertEquals(username, result.getIdentity()); Assert.assertEquals(username, result.getIdentity());
@ -143,8 +137,10 @@ public class DBCredentialsValidatorTest
String username = "userA"; String username = "userA";
String password = "badpassword"; String password = "badpassword";
expectedException.expect(BasicSecurityAuthenticationException.class); Exception exception = Assert.assertThrows(
expectedException.expectMessage(Access.DEFAULT_ERROR_MESSAGE); BasicSecurityAuthenticationException.class,
validator.validateCredentials(authenticatorName, authorizerName, username, password.toCharArray()); () -> VALIDATOR.validateCredentials(authenticatorName, authorizerName, username, password.toCharArray())
);
Assert.assertEquals(Access.DEFAULT_ERROR_MESSAGE, exception.getMessage());
} }
} }