Changed the cached hashing algorithm for cached realms
Now the passwords are hashed in-memory using SHA2 by default (instead of original bcrypt). Also, it's now possible to configure the in-memory hashing algorithm. Original commit: elastic/x-pack-elasticsearch@e2d1b3116b
This commit is contained in:
parent
521ebe4672
commit
b7dac66c8a
|
@ -7,6 +7,8 @@ package org.elasticsearch.shield;
|
|||
|
||||
import org.elasticsearch.shield.authz.SystemRole;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* An authenticated user
|
||||
*/
|
||||
|
@ -48,6 +50,26 @@ public abstract class User {
|
|||
public String[] roles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Simple simple = (Simple) o;
|
||||
|
||||
if (!Arrays.equals(roles, simple.roles)) return false;
|
||||
if (!username.equals(simple.username)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = username.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(roles);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static class System extends User {
|
||||
|
@ -67,6 +89,7 @@ public abstract class User {
|
|||
public String[] roles() {
|
||||
return ROLES;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public class ESUsersModule extends AbstractShieldModule.Node {
|
|||
@Override
|
||||
protected void configureNode() {
|
||||
if (enabled) {
|
||||
bind(Realm.class).annotatedWith(named(ESUsersRealm.TYPE)).to(ESUsersRealm.class).asEagerSingleton();
|
||||
bind(ESUsersRealm.class).asEagerSingleton();
|
||||
bind(UserPasswdStore.class).annotatedWith(named("file")).to(FileUserPasswdStore.class).asEagerSingleton();
|
||||
bind(UserRolesStore.class).annotatedWith(named("file")).to(FileUserRolesStore.class).asEagerSingleton();
|
||||
} else {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.shield.authc.esusers;
|
||||
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.name.Named;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
@ -14,7 +13,7 @@ import org.elasticsearch.rest.RestController;
|
|||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.shield.authc.AuthenticationToken;
|
||||
import org.elasticsearch.shield.authc.Realm;
|
||||
import org.elasticsearch.shield.authc.support.CachingUsernamePasswordRealm;
|
||||
import org.elasticsearch.shield.authc.support.UserPasswdStore;
|
||||
import org.elasticsearch.shield.authc.support.UserRolesStore;
|
||||
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
||||
|
@ -23,7 +22,7 @@ import org.elasticsearch.transport.TransportMessage;
|
|||
/**
|
||||
*
|
||||
*/
|
||||
public class ESUsersRealm extends AbstractComponent implements Realm<UsernamePasswordToken> {
|
||||
public class ESUsersRealm extends CachingUsernamePasswordRealm {
|
||||
|
||||
public static final String TYPE = "esusers";
|
||||
|
||||
|
@ -60,7 +59,7 @@ public class ESUsersRealm extends AbstractComponent implements Realm<UsernamePas
|
|||
}
|
||||
|
||||
@Override
|
||||
public User authenticate(UsernamePasswordToken token) {
|
||||
protected User doAuthenticate(UsernamePasswordToken token) {
|
||||
if (userPasswdStore == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -30,8 +30,11 @@ public abstract class CachingUsernamePasswordRealm extends AbstractComponent imp
|
|||
|
||||
private final Cache<String, UserWithHash> cache;
|
||||
|
||||
private final Hasher hasher;
|
||||
|
||||
protected CachingUsernamePasswordRealm(Settings settings) {
|
||||
super(settings);
|
||||
hasher = Hasher.resolve(componentSettings.get("cache.hash_algo", null), Hasher.SHA2);
|
||||
TimeValue ttl = componentSettings.getAsTime(CACHE_TTL, DEFAULT_TTL);
|
||||
if (ttl.millis() > 0) {
|
||||
cache = CacheBuilder.newBuilder()
|
||||
|
@ -91,7 +94,7 @@ public abstract class CachingUsernamePasswordRealm extends AbstractComponent imp
|
|||
if (user == null) {
|
||||
throw new AuthenticationException("Could not authenticate ['" + token.principal() + "]");
|
||||
}
|
||||
return new UserWithHash(user, token.credentials());
|
||||
return new UserWithHash(user, token.credentials(), hasher);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -116,13 +119,15 @@ public abstract class CachingUsernamePasswordRealm extends AbstractComponent imp
|
|||
public static class UserWithHash {
|
||||
User user;
|
||||
char[] hash;
|
||||
public UserWithHash(User user, SecuredString password){
|
||||
Hasher hasher;
|
||||
public UserWithHash(User user, SecuredString password, Hasher hasher){
|
||||
this.user = user;
|
||||
this.hash = Hasher.HTPASSWD.hash(password);
|
||||
this.hash = hasher.hash(password);
|
||||
this.hasher = hasher;
|
||||
}
|
||||
|
||||
public boolean verify(SecuredString password){
|
||||
return Hasher.HTPASSWD.verify(password, hash);
|
||||
return hasher.verify(password, hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.apache.commons.codec.digest.Md5Crypt;
|
|||
import org.apache.commons.codec.digest.Sha2Crypt;
|
||||
import org.elasticsearch.ElasticsearchIllegalArgumentException;
|
||||
import org.elasticsearch.common.os.OsUtils;
|
||||
import org.elasticsearch.shield.ShieldSettingsException;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
|
@ -147,6 +148,10 @@ public enum Hasher {
|
|||
}
|
||||
switch (name.toLowerCase(Locale.ROOT)) {
|
||||
case "htpasswd" : return HTPASSWD;
|
||||
case "bcrypt" : return BCRYPT;
|
||||
case "sha1" : return SHA1;
|
||||
case "sha2" : return SHA2;
|
||||
case "md5" : return MD5;
|
||||
default:
|
||||
return defaultHasher;
|
||||
}
|
||||
|
@ -155,7 +160,7 @@ public enum Hasher {
|
|||
public static Hasher resolve(String name) {
|
||||
Hasher hasher = resolve(name, null);
|
||||
if (hasher == null) {
|
||||
throw new ElasticsearchIllegalArgumentException("Unknown hash function [" + name + "]");
|
||||
throw new ShieldSettingsException("Unknown hash function [" + name + "]");
|
||||
}
|
||||
return hasher;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.shield.authc.esusers;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.action.Action;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.ActionRequest;
|
||||
|
@ -28,9 +29,9 @@ import org.hamcrest.Matchers;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.arrayContaining;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
|
@ -70,6 +71,19 @@ public class ESUsersRealmTests extends ElasticsearchTestCase {
|
|||
assertThat(user.roles(), arrayContaining("role1", "role2"));
|
||||
}
|
||||
|
||||
@Test @Repeat(iterations = 20)
|
||||
public void testAuthenticate_Caching() throws Exception {
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put("shield.authc.esusers.cache.hash_algo", Hasher.values()[randomIntBetween(0, Hasher.values().length - 1)].name().toLowerCase(Locale.ROOT))
|
||||
.build();
|
||||
MockUserPasswdStore userPasswdStore = new MockUserPasswdStore("user1", "test123");
|
||||
MockUserRolesStore userRolesStore = new MockUserRolesStore("user1", "role1", "role2");
|
||||
ESUsersRealm realm = new ESUsersRealm(settings, userPasswdStore, userRolesStore, restController);
|
||||
User user1 = realm.authenticate(new UsernamePasswordToken("user1", SecuredStringTests.build("test123")));
|
||||
User user2 = realm.authenticate(new UsernamePasswordToken("user1", SecuredStringTests.build("test123")));
|
||||
assertThat(user1, sameInstance(user2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToken() throws Exception {
|
||||
Settings settings = ImmutableSettings.builder().build();
|
||||
|
|
Loading…
Reference in New Issue