Merge branch 'master' of https://github.com/elasticsearch/elasticsearch-shield into doc-feedback
Conflicts: docs/public/04-authorization.asciidoc docs/public/clients/logstash.asciidoc Original commit: elastic/x-pack-elasticsearch@699aa52379
This commit is contained in:
commit
b3789a74e4
|
@ -0,0 +1,13 @@
|
|||
admin:
|
||||
cluster: all
|
||||
indices:
|
||||
'.*': all
|
||||
|
||||
power_user:
|
||||
cluster: monitor
|
||||
indices:
|
||||
'.*': all
|
||||
|
||||
user:
|
||||
indices:
|
||||
'.*': read
|
|
@ -0,0 +1 @@
|
|||
*43QåÆ]Ûùð/÷ô<14>>eû.²¾g^lçH¶ûgu«•±Ê ’O/Gaoƒ˜ Ⱥâ•rr³ø´èk_ËÐ2û*¹–©m•?>Æд,”]‡<>ÆUž’¤å¦p¶I婳ò¼£¸sOYwu†¹äŸK’•¨°‘+_¹0
|
|
@ -0,0 +1,5 @@
|
|||
admin-bcrypt:$2a$10$5uCJHPn3p0ZPQp6rIIgcDO0VZ3urZZmA.egHiy/WknxIkAyZXPGpy
|
||||
admin-plain:{plain}changeme
|
||||
admin-sha:{SHA}+pvrmeQCmtWmYVOZ57uuITVghrM=
|
||||
admin-apr:$apr1$fCQ4kkwA$ETvNx37ooOcdau5a61S/s.
|
||||
admin-sha2:$5$mw0LEbLr$s57Rbo0wfH8Z690Dc0..VgC1qn/a5h73bbpt8kql8B4
|
|
@ -0,0 +1 @@
|
|||
admin:admin-bcrypt,admin-sha,admin-plain,admin-apr,admin-sha2
|
12
.esvmrc
12
.esvmrc
|
@ -3,12 +3,15 @@
|
|||
"plugins": [ "lmenezes/elasticsearch-kopf", { "name": "shield", "path" : "file:./target/releases/elasticsearch-shield-1.0.0-SNAPSHOT.zip" } ],
|
||||
"config" : {
|
||||
"cluster": { "name": "shield" },
|
||||
"indices.store.throttle.max_bytes_per_sec": "100mb",
|
||||
"discovery" : {
|
||||
"type" : "zen",
|
||||
"zen.ping.multicast.enabled": false,
|
||||
"zen.ping.unicast.hosts" : [ "localhost:9300", "localhost:9301" ]
|
||||
},
|
||||
"shield" : {
|
||||
"enabled" : true,
|
||||
"system_key.file": ".esvm-shield-config/system_key",
|
||||
"audit.enabled" : false,
|
||||
"transport.ssl": true,
|
||||
"http.ssl": true,
|
||||
|
@ -17,7 +20,14 @@
|
|||
"keystore_password" : "testnode",
|
||||
"truststore" : "src/test/resources/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks",
|
||||
"truststore_password" : "testnode"
|
||||
}
|
||||
},
|
||||
"authc": {
|
||||
"esusers.files" : {
|
||||
"users" : ".esvm-shield-config/users",
|
||||
"users_roles" : ".esvm-shield-config/users_roles"
|
||||
}
|
||||
},
|
||||
"authz.store.files.roles" : ".esvm-shield-config/roles.yml"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -59,6 +59,7 @@ public class SecurityFilter extends AbstractComponent {
|
|||
|
||||
AuthenticationToken token = authcService.token(action, request, defaultToken);
|
||||
User user = authcService.authenticate(action, request, token);
|
||||
|
||||
authzService.authorize(user, action, request);
|
||||
return user;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -62,6 +62,11 @@ public class InternalAuthenticationService extends AbstractComponent implements
|
|||
for (Realm realm : realms) {
|
||||
token = realm.token(message);
|
||||
if (token != null) {
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Realm [{}] resolved auth token [{}] from transport request with action [{}]", realm.type(), token.principal(), action);
|
||||
}
|
||||
|
||||
message.putInContext(TOKEN_CTX_KEY, token);
|
||||
return token;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ import static org.elasticsearch.shield.support.Automatons.patterns;
|
|||
*/
|
||||
public abstract class Privilege<P extends Privilege<P>> {
|
||||
|
||||
static final String SUB_ACTION_SUFFIX_PATTERN = ".*";
|
||||
static final String SUB_ACTION_SUFFIX_PATTERN = "*";
|
||||
|
||||
public static final System SYSTEM = new System();
|
||||
|
||||
|
@ -76,7 +76,9 @@ public abstract class Privilege<P extends Privilege<P>> {
|
|||
public static class System extends Privilege<System> {
|
||||
|
||||
protected static final Predicate<String> PREDICATE = new AutomatonPredicate(patterns(
|
||||
"internal:.*"
|
||||
"internal:*",
|
||||
"indices:monitor/*", // added for marvel
|
||||
"cluster:monitor/*" // added for marvel
|
||||
));
|
||||
|
||||
private System() {
|
||||
|
@ -97,18 +99,18 @@ public abstract class Privilege<P extends Privilege<P>> {
|
|||
public static class Index extends AutomatonPrivilege<Index> {
|
||||
|
||||
public static final Index NONE = new Index(Name.NONE, Automata.makeEmpty());
|
||||
public static final Index ALL = new Index(Name.ALL, "indices:.*");
|
||||
public static final Index MANAGE = new Index("manage", "indices:monitor/.*", "indices:admin/.*");
|
||||
public static final Index ALL = new Index(Name.ALL, "indices:*");
|
||||
public static final Index MANAGE = new Index("manage", "indices:monitor/*", "indices:admin/*");
|
||||
public static final Index CREATE_INDEX = new Index("create_index", "indices:admin/create");
|
||||
public static final Index MONITOR = new Index("monitor", "indices:monitor/.*");
|
||||
public static final Index DATA_ACCESS = new Index("data_access", "indices:data/.*");
|
||||
public static final Index CRUD = new Index("crud", "indices:data/write/.*", "indices:data/read/.*");
|
||||
public static final Index READ = new Index("read", "indices:data/read/.*");
|
||||
public static final Index SEARCH = new Index("search", SearchAction.NAME + ".*", GetAction.NAME + ".*");
|
||||
public static final Index GET = new Index("get", GetAction.NAME + ".*");
|
||||
public static final Index INDEX = new Index("index", "indices:data/write/index.*", "indices:data/write/update");
|
||||
public static final Index DELETE = new Index("delete", "indices:data/write/delete.*");
|
||||
public static final Index WRITE = new Index("write", "indices:data/write/.*");
|
||||
public static final Index MONITOR = new Index("monitor", "indices:monitor/*");
|
||||
public static final Index DATA_ACCESS = new Index("data_access", "indices:data/*");
|
||||
public static final Index CRUD = new Index("crud", "indices:data/write/*", "indices:data/read/*");
|
||||
public static final Index READ = new Index("read", "indices:data/read/*");
|
||||
public static final Index SEARCH = new Index("search", SearchAction.NAME + "*", GetAction.NAME + "*");
|
||||
public static final Index GET = new Index("get", GetAction.NAME + "*");
|
||||
public static final Index INDEX = new Index("index", "indices:data/write/index*", "indices:data/write/update");
|
||||
public static final Index DELETE = new Index("delete", "indices:data/write/delete*");
|
||||
public static final Index WRITE = new Index("write", "indices:data/write/*");
|
||||
public static final Index BENCHMARK = new Index("benchmark", "indices:data/benchmark");
|
||||
|
||||
private static final Index[] values = new Index[] {
|
||||
|
@ -196,8 +198,8 @@ public abstract class Privilege<P extends Privilege<P>> {
|
|||
public static class Cluster extends AutomatonPrivilege<Cluster> {
|
||||
|
||||
public static final Cluster NONE = new Cluster(Name.NONE, Automata.makeEmpty());
|
||||
public static final Cluster ALL = new Cluster(Name.ALL, "cluster:.*", "indices:admin/template/.*");
|
||||
public static final Cluster MONITOR = new Cluster("monitor", "cluster:monitor/.*");
|
||||
public static final Cluster ALL = new Cluster(Name.ALL, "cluster:*", "indices:admin/template/*");
|
||||
public static final Cluster MONITOR = new Cluster("monitor", "cluster:monitor/*");
|
||||
|
||||
private static final Cluster[] values = new Cluster[] { NONE, ALL, MONITOR };
|
||||
|
||||
|
@ -272,7 +274,7 @@ public abstract class Privilege<P extends Privilege<P>> {
|
|||
}
|
||||
|
||||
static String actionToPattern(String text) {
|
||||
return text.replace(":", "\\:") + SUB_ACTION_SUFFIX_PATTERN;
|
||||
return text + SUB_ACTION_SUFFIX_PATTERN;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
@ -7,9 +7,12 @@ package org.elasticsearch.shield.support;
|
|||
|
||||
import org.apache.lucene.util.automaton.Automata;
|
||||
import org.apache.lucene.util.automaton.Automaton;
|
||||
import org.apache.lucene.util.automaton.Operations;
|
||||
import org.apache.lucene.util.automaton.RegExp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.lucene.util.automaton.MinimizationOperations.minimize;
|
||||
import static org.apache.lucene.util.automaton.Operations.*;
|
||||
|
@ -19,20 +22,30 @@ import static org.apache.lucene.util.automaton.Operations.*;
|
|||
*/
|
||||
public final class Automatons {
|
||||
|
||||
static final char WILDCARD_STRING = '*'; // String equality with support for wildcards
|
||||
static final char WILDCARD_CHAR = '?'; // Char equality with support for wildcards
|
||||
static final char WILDCARD_ESCAPE = '\\'; // Escape character
|
||||
|
||||
private Automatons() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns an automaton that will represent the union of all the given patterns.
|
||||
*/
|
||||
public static Automaton patterns(String... patterns) {
|
||||
if (patterns.length == 0) {
|
||||
return Automata.makeEmpty();
|
||||
}
|
||||
Automaton automaton = new RegExp(patterns[0]).toAutomaton();
|
||||
Automaton automaton = pattern(patterns[0]);
|
||||
for (String pattern : patterns) {
|
||||
automaton = union(automaton, new RegExp(pattern).toAutomaton());
|
||||
automaton = union(automaton, pattern(pattern));
|
||||
}
|
||||
return determinize(minimize(automaton));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns an automaton that will represent the union of all the given patterns.
|
||||
*/
|
||||
public static Automaton patterns(Collection<String> patterns) {
|
||||
if (patterns.isEmpty()) {
|
||||
return Automata.makeEmpty();
|
||||
|
@ -40,14 +53,61 @@ public final class Automatons {
|
|||
Automaton automaton = null;
|
||||
for (String pattern : patterns) {
|
||||
if (automaton == null) {
|
||||
automaton = new RegExp(pattern).toAutomaton();
|
||||
automaton = pattern(pattern);
|
||||
} else {
|
||||
automaton = union(automaton, new RegExp(pattern).toAutomaton());
|
||||
automaton = union(automaton, pattern(pattern));
|
||||
}
|
||||
}
|
||||
return determinize(minimize(automaton));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns an automaton that represents the given pattern.
|
||||
*/
|
||||
static Automaton pattern(String pattern) {
|
||||
if (pattern.startsWith("/")) { // it's a lucene regexp
|
||||
if (pattern.length() == 1 || !pattern.endsWith("/")) {
|
||||
throw new IllegalArgumentException("Invalid pattern [" + pattern + "]. Patterns starting with '/' " +
|
||||
"indicate regular expression pattern and therefore must also end with '/'." +
|
||||
" Other patterns (those that do not start with '/') will be treated as simple wildcard patterns");
|
||||
}
|
||||
String regex = pattern.substring(1, pattern.length() - 1);
|
||||
return new RegExp(regex).toAutomaton();
|
||||
}
|
||||
return wildcard(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns an automaton that represents the given pattern.
|
||||
*/
|
||||
static Automaton wildcard(String text) {
|
||||
List<Automaton> automata = new ArrayList<>();
|
||||
for (int i = 0; i < text.length();) {
|
||||
final int c = text.codePointAt(i);
|
||||
int length = Character.charCount(c);
|
||||
switch(c) {
|
||||
case WILDCARD_STRING:
|
||||
automata.add(Automata.makeAnyString());
|
||||
break;
|
||||
case WILDCARD_CHAR:
|
||||
automata.add(Automata.makeAnyChar());
|
||||
break;
|
||||
case WILDCARD_ESCAPE:
|
||||
// add the next codepoint instead, if it exists
|
||||
if (i + length < text.length()) {
|
||||
final int nextChar = text.codePointAt(i + length);
|
||||
length += Character.charCount(nextChar);
|
||||
automata.add(Automata.makeChar(nextChar));
|
||||
break;
|
||||
} // else fallthru, lenient parsing with a trailing \
|
||||
default:
|
||||
automata.add(Automata.makeChar(c));
|
||||
}
|
||||
i += length;
|
||||
}
|
||||
return Operations.concatenate(automata);
|
||||
}
|
||||
|
||||
public static Automaton unionAndDeterminize(Automaton a1, Automaton a2) {
|
||||
return determinize(union(a1, a2));
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ public class MultipleIndicesPermissionsTests extends ShieldIntegrationTest {
|
|||
public static final String ROLES = "user:\n" +
|
||||
" cluster: all\n" +
|
||||
" indices:\n" +
|
||||
" '.*': manage\n" +
|
||||
" '.*': write\n" +
|
||||
" '*': manage\n" +
|
||||
" '/.*/': write\n" +
|
||||
" 'test': read\n" +
|
||||
" 'test1': read\n";
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -25,9 +25,9 @@ public class PermissionTests extends ElasticsearchTestCase {
|
|||
@Before
|
||||
public void init() {
|
||||
Permission.Global.Builder builder = Permission.Global.builder(mock(AuthorizationService.class));
|
||||
builder.add(union(SEARCH, MONITOR), "test_.*", "foo.*");
|
||||
builder.add(union(READ), "baz_.*foo", "fool.*bar");
|
||||
builder.add(union(MONITOR), "bar.*");
|
||||
builder.add(union(SEARCH, MONITOR), "test_*", "/foo.*/");
|
||||
builder.add(union(READ), "baz_*foo", "/fool.*bar/");
|
||||
builder.add(union(MONITOR), "/bar.*/");
|
||||
permission = builder.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.shield.authz;
|
|||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.ElasticsearchIllegalArgumentException;
|
||||
import org.elasticsearch.common.base.Predicate;
|
||||
import org.elasticsearch.shield.support.AutomatonPredicate;
|
||||
import org.elasticsearch.shield.support.Automatons;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
|
@ -147,4 +148,14 @@ public class PrivilegeTests extends ElasticsearchTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystem() throws Exception {
|
||||
Predicate<String> predicate = Privilege.SYSTEM.predicate();
|
||||
assertThat(predicate.apply("indices:monitor/whatever"), is(true));
|
||||
assertThat(predicate.apply("cluster:monitor/whatever"), is(true));
|
||||
assertThat(predicate.apply("internal:whatever"), is(true));
|
||||
assertThat(predicate.apply("indices:whatever"), is(false));
|
||||
assertThat(predicate.apply("cluster:whatever"), is(false));
|
||||
assertThat(predicate.apply("whatever"), is(false));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.authz;
|
||||
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class SystemRoleTests extends ElasticsearchTestCase {
|
||||
|
||||
@Test
|
||||
public void testCheck() throws Exception {
|
||||
assertThat(SystemRole.INSTANCE.check("indices:monitor/whatever"), is(true));
|
||||
assertThat(SystemRole.INSTANCE.check("cluster:monitor/whatever"), is(true));
|
||||
assertThat(SystemRole.INSTANCE.check("internal:whatever"), is(true));
|
||||
assertThat(SystemRole.INSTANCE.check("indices:whatever"), is(false));
|
||||
assertThat(SystemRole.INSTANCE.check("cluster:whatever"), is(false));
|
||||
assertThat(SystemRole.INSTANCE.check("whatever"), is(false));
|
||||
}
|
||||
}
|
|
@ -29,8 +29,8 @@ public class IndicesResolverIntegrationTests extends ShieldIntegrationTest {
|
|||
return DEFAULT_ROLE + ":\n" +
|
||||
" cluster: ALL\n" +
|
||||
" indices:\n" +
|
||||
" '.*': manage,write\n" +
|
||||
" 'test.*': read\n";
|
||||
" '*': manage,write\n" +
|
||||
" '/test.*/': read\n";
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -89,7 +89,7 @@ public class FileRolesStoreTests extends ElasticsearchTestCase {
|
|||
group = permission.indices().groups()[0];
|
||||
assertThat(group.indices(), notNullValue());
|
||||
assertThat(group.indices().length, is(1));
|
||||
assertThat(group.indices()[0], equalTo(".*_.*"));
|
||||
assertThat(group.indices()[0], equalTo("/.*_.*/"));
|
||||
assertThat(group.privilege(), notNullValue());
|
||||
assertThat(group.privilege().isAlias(Privilege.Index.union(Privilege.Index.READ, Privilege.Index.WRITE)), is(true));
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ public class FileRolesStoreTests extends ElasticsearchTestCase {
|
|||
@Test(expected = ElasticsearchException.class)
|
||||
public void testThatInvalidYAMLThrowsElasticsearchException() throws Exception {
|
||||
File file = tempFolder.newFile();
|
||||
com.google.common.io.Files.write("user: cluster: ALL indices: '.*': ALL".getBytes(Charsets.UTF_8), file);
|
||||
com.google.common.io.Files.write("user: cluster: ALL indices: '*': ALL".getBytes(Charsets.UTF_8), file);
|
||||
FileRolesStore.parseFile(file.toPath(), logger, mock(AuthorizationService.class));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.support;
|
||||
|
||||
import org.apache.lucene.util.automaton.Automaton;
|
||||
import org.apache.lucene.util.automaton.CharacterRunAutomaton;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.elasticsearch.shield.support.Automatons.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class AutomatonsTests extends ElasticsearchTestCase {
|
||||
|
||||
@Test
|
||||
public void testPatterns_UnionOfMultiplePatterns() throws Exception {
|
||||
assertMatch(patterns("/fo.*/", "ba*"), "foo");
|
||||
assertMatch(patterns("/fo.*/", "ba*"), "bar");
|
||||
assertMismatch(patterns("/fo.*/", "ba*"), "zipfoo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPattern_Single() throws Exception {
|
||||
assertMatch(pattern("/.*st/"), "test");
|
||||
assertMatch(pattern("/t.*st/"), "test");
|
||||
assertMatch(pattern("/tes*./"), "test");
|
||||
assertMatch(pattern("/test/"), "test");
|
||||
assertMismatch(pattern("/.*st/"), "tet");
|
||||
assertMatch(pattern("*st"), "test");
|
||||
assertMatch(pattern("t*t"), "test");
|
||||
assertMatch(pattern("t?st"), "test");
|
||||
assertMismatch(pattern("t?t"), "test");
|
||||
assertMatch(pattern("tes*"), "test");
|
||||
assertMatch(pattern("test"), "test");
|
||||
assertMismatch(pattern("*st"), "tet");
|
||||
assertInvalidPattern("/test");
|
||||
assertInvalidPattern("/te*");
|
||||
assertInvalidPattern("/te.*");
|
||||
assertMismatch(pattern(".*st"), "test");
|
||||
assertMatch(pattern("*st\\"), "test\\");
|
||||
assertMatch(pattern("tes.*/"), "tes.t/");
|
||||
assertMatch(pattern("\\/test"), "/test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWildcard() throws Exception {
|
||||
assertMatch(wildcard("*st"), "test");
|
||||
assertMatch(wildcard("t*st"), "test");
|
||||
assertMatch(wildcard("tes*"), "test");
|
||||
assertMatch(wildcard("test"), "test");
|
||||
assertMismatch(wildcard("*st"), "tet");
|
||||
assertMismatch(wildcard("t\\*st"), "test");
|
||||
assertMatch(wildcard("t\\*st"), "t*st");
|
||||
}
|
||||
|
||||
private void assertMatch(Automaton automaton, String text) {
|
||||
CharacterRunAutomaton runAutomaton = new CharacterRunAutomaton(automaton);
|
||||
assertThat(runAutomaton.run(text), is(true));
|
||||
}
|
||||
|
||||
private void assertMismatch(Automaton automaton, String text) {
|
||||
CharacterRunAutomaton runAutomaton = new CharacterRunAutomaton(automaton);
|
||||
assertThat(runAutomaton.run(text), is(false));
|
||||
}
|
||||
|
||||
private void assertInvalidPattern(String text) {
|
||||
try {
|
||||
pattern(text);
|
||||
fail("expected an error on invalid pattern [" + text + "]");
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
}
|
|
@ -53,7 +53,7 @@ public abstract class ShieldIntegrationTest extends ElasticsearchIntegrationTest
|
|||
public static final String CONFIG_ROLE_ALLOW_ALL = DEFAULT_ROLE + ":\n" +
|
||||
" cluster: ALL\n" +
|
||||
" indices:\n" +
|
||||
" '.*': ALL\n";
|
||||
" '*': ALL\n";
|
||||
|
||||
@ClassRule
|
||||
public static TemporaryFolder tmpFolder = new TemporaryFolder();
|
||||
|
|
|
@ -56,7 +56,7 @@ public class ShieldRestTests extends ElasticsearchRestTests {
|
|||
public static final String CONFIG_ROLE_ALLOW_ALL = DEFAULT_ROLE + ":\n" +
|
||||
" cluster: ALL\n" +
|
||||
" indices:\n" +
|
||||
" '.*': ALL\n";
|
||||
" '*': ALL\n";
|
||||
|
||||
static {
|
||||
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
##############
|
||||
## Logstash configuration for shield load testing
|
||||
##
|
||||
## This configuration makes performance testing against SHIELD a lot easier
|
||||
## The generator input creates 1000000 very small documents
|
||||
## The elasticsearch outputs (you should choose only one!) then can be configured to use
|
||||
## one of the configured setups (shield enabled/disabled, ssl enabled/disabled, HTTP or transport)
|
||||
## to find out, what kind of overhead the plugin has
|
||||
##
|
||||
## Note: Please make sure you are using a logstash instance, that supports shield (as of 10-2014, this resided in its own branch)
|
||||
##
|
||||
#############
|
||||
## Running the tests
|
||||
## 1. Start an elasticsearch cluster using esvm (this one only starts one node): esvm shield -n 1
|
||||
## 2. Start logstash against it: ../logstash-output-es-shield/build/tarball/logstash-1.4.2/bin/logstash -p ~/.esvm/1.4.0.Beta1/plugins/ agent -f src/test/resources/logstash-shield.conf
|
||||
#############
|
||||
|
||||
input {
|
||||
generator {
|
||||
count => 1000000
|
||||
}
|
||||
}
|
||||
|
||||
output {
|
||||
|
||||
##########
|
||||
## HTTP
|
||||
##########
|
||||
|
||||
# Use this when shield is disabled
|
||||
# elasticsearch {
|
||||
# protocol => 'http'
|
||||
# }
|
||||
|
||||
# Shield enabled, SSL disabled
|
||||
elasticsearch {
|
||||
protocol => 'http'
|
||||
# can be one of admin-plain, admin-bcrypt, admin-sha, admin-apr, admin-sha2
|
||||
user => 'admin-plain'
|
||||
password => 'changeme'
|
||||
}
|
||||
|
||||
# Shield enabled, SSL enabled
|
||||
# elasticsearch {
|
||||
# protocol => 'http'
|
||||
# user => 'admin'
|
||||
# password => 'changeme'
|
||||
# ssl => 'true'
|
||||
# ssl_verify => false
|
||||
# }
|
||||
|
||||
|
||||
|
||||
##########
|
||||
## Transport Client
|
||||
##########
|
||||
|
||||
# Use this when shield is disabled
|
||||
# elasticsearch {
|
||||
# protocol => 'transport'
|
||||
# }
|
||||
|
||||
# Shield enabled, SSL disabled
|
||||
# elasticsearch {
|
||||
# protocol => 'transport'
|
||||
# user => 'admin'
|
||||
# password => 'changeme'
|
||||
# }
|
||||
|
||||
# Shield enabled, SSL enabled
|
||||
# elasticsearch {
|
||||
# protocol => 'transport'
|
||||
# user => 'admin'
|
||||
# password => 'changeme'
|
||||
# ssl => 'true'
|
||||
# keystore => 'src/test/resources/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks'
|
||||
# keystore_password => 'testclient'
|
||||
# truststore => 'src/test/resources/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks'
|
||||
# truststore_password => 'testclient'
|
||||
# }
|
||||
}
|
||||
|
|
@ -9,5 +9,5 @@ role2:
|
|||
|
||||
role3:
|
||||
indices:
|
||||
'.*_.*': READ, WRITE
|
||||
'/.*_.*/': READ, WRITE
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
user:
|
||||
cluster: ALL
|
||||
indices:
|
||||
'.*': ALL
|
||||
'*': ALL
|
Loading…
Reference in New Issue