diff --git a/src/main/java/org/elasticsearch/shield/authz/Privilege.java b/src/main/java/org/elasticsearch/shield/authz/Privilege.java
index e107865030b..6432bfc4ba4 100644
--- a/src/main/java/org/elasticsearch/shield/authz/Privilege.java
+++ b/src/main/java/org/elasticsearch/shield/authz/Privilege.java
@@ -13,6 +13,7 @@ import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.action.admin.indices.create.CreateIndexAction;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.action.search.SearchAction;
+import org.elasticsearch.action.suggest.SuggestAction;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.base.Predicate;
import org.elasticsearch.common.cache.CacheBuilder;
@@ -114,14 +115,15 @@ public abstract class Privilege
> {
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 SEARCH = new Index("search", SearchAction.NAME + "*", GetAction.NAME + "*", SuggestAction.NAME + "*");
public static final Index GET = new Index("get", GetAction.NAME + "*");
+ public static final Index SUGGEST = new Index("suggest", SuggestAction.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/*");
private static final Index[] values = new Index[] {
- NONE, ALL, MANAGE, CREATE_INDEX, MANAGE_ALIASES, MONITOR, DATA_ACCESS, CRUD, READ, SEARCH, GET, INDEX, DELETE, WRITE
+ NONE, ALL, MANAGE, CREATE_INDEX, MANAGE_ALIASES, MONITOR, DATA_ACCESS, CRUD, READ, SEARCH, GET, SUGGEST, INDEX, DELETE, WRITE
};
public static final Predicate ACTION_MATCHER = Privilege.Index.ALL.predicate();
diff --git a/src/main/java/org/elasticsearch/shield/signature/InternalSignatureService.java b/src/main/java/org/elasticsearch/shield/signature/InternalSignatureService.java
index b131e8e3d3f..e7b5a519737 100644
--- a/src/main/java/org/elasticsearch/shield/signature/InternalSignatureService.java
+++ b/src/main/java/org/elasticsearch/shield/signature/InternalSignatureService.java
@@ -8,7 +8,6 @@ package org.elasticsearch.shield.signature;
import org.apache.commons.codec.binary.Base64;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.base.Charsets;
-import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
diff --git a/src/test/java/org/elasticsearch/integration/SearchPermissionsTests.java b/src/test/java/org/elasticsearch/integration/SearchPermissionsTests.java
new file mode 100644
index 00000000000..7db32bd175b
--- /dev/null
+++ b/src/test/java/org/elasticsearch/integration/SearchPermissionsTests.java
@@ -0,0 +1,141 @@
+/*
+ * 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.integration;
+
+import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.action.index.IndexResponse;
+import org.elasticsearch.action.suggest.SuggestResponse;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.search.suggest.SuggestBuilders;
+import org.elasticsearch.shield.authc.support.SecuredStringTests;
+import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
+import org.elasticsearch.shield.authz.AuthorizationException;
+import org.elasticsearch.test.ShieldIntegrationTest;
+import org.junit.Test;
+
+import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
+import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
+import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope;
+import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+
+@ClusterScope(scope = Scope.SUITE)
+public class SearchPermissionsTests extends ShieldIntegrationTest {
+
+ @Override
+ protected String configRoles() {
+ return super.configRoles() + "\n" +
+ "\n" +
+ "suggest_role:\n" +
+ " indices:\n" +
+ " 'a': suggest\n" +
+ "\n" +
+ "search_role:\n" +
+ " indices:\n" +
+ " 'a': search\n" +
+ "\n" +
+ "get_role:\n" +
+ " indices:\n" +
+ " 'a': get\n";
+ }
+
+ @Override
+ protected String configUsers() {
+ return super.configUsers() +
+ "suggest_user:{plain}passwd\n" +
+ "search_user:{plain}passwd\n" +
+ "get_user:{plain}passwd\n";
+ }
+
+ @Override
+ protected String configUsersRoles() {
+ return super.configUsersRoles() +
+ "suggest_role:suggest_user\n" +
+ "search_role:search_user\n" +
+ "get_role:get_user";
+ }
+
+ /**
+ * testing both "search" and "suggest" privileges can execute the suggest API
+ */
+ @Test
+ public void testSuggestAPI() throws Exception {
+ IndexResponse indexResponse = index("a", "type", jsonBuilder()
+ .startObject()
+ .field("name", "value")
+ .endObject());
+ assertThat(indexResponse.isCreated(), is(true));
+
+ refresh();
+
+ Client client = internalCluster().transportClient();
+
+ SuggestResponse suggestResponse = client.prepareSuggest("a")
+ .putHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, userHeader("suggest_user", "passwd"))
+ .addSuggestion(SuggestBuilders.termSuggestion("name").field("name").text("val")).get();
+ assertNoFailures(suggestResponse);
+ assertThat(suggestResponse.getSuggest().size(), is(1));
+
+ suggestResponse = client.prepareSuggest("a")
+ .putHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, userHeader("search_user", "passwd"))
+ .addSuggestion(SuggestBuilders.termSuggestion("name").field("name").text("val")).get();
+ assertNoFailures(suggestResponse);
+ assertThat(suggestResponse.getSuggest().size(), is(1));
+
+ try {
+ client.prepareSearch("a")
+ .putHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, userHeader("suggest_user", "passwd"))
+ .get();
+ fail("a user with only a suggest privilege cannot execute search");
+ } catch (AuthorizationException e) {
+ logger.error("failed to search", e);
+ // expected
+ }
+ }
+
+ /**
+ * testing both "search" and "get" privileges can execute the get API
+ */
+ @Test
+ public void testGetAPI() throws Exception {
+ IndexResponse indexResponse = index("a", "type", jsonBuilder()
+ .startObject()
+ .field("name", "value")
+ .endObject());
+ assertThat(indexResponse.isCreated(), is(true));
+
+ refresh();
+
+ Client client = internalCluster().transportClient();
+
+ GetResponse getResponse = client.prepareGet("a", "type", indexResponse.getId())
+ .putHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, userHeader("get_user", "passwd"))
+ .get();
+ assertNotNull(getResponse);
+ assertThat(getResponse.getId(), equalTo(indexResponse.getId()));
+
+ getResponse = client.prepareGet("a", "type", indexResponse.getId())
+ .putHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, userHeader("search_user", "passwd"))
+ .get();
+ assertNotNull(getResponse);
+ assertThat(getResponse.getId(), equalTo(indexResponse.getId()));
+
+ try {
+ client.prepareSearch("a")
+ .putHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, userHeader("get_user", "passwd"))
+ .get();
+ fail("a user with only a get privilege cannot execute search");
+ } catch (AuthorizationException e) {
+ logger.error("failed to search", e);
+ // expected
+ }
+ }
+
+ private static String userHeader(String username, String password) {
+ return UsernamePasswordToken.basicAuthHeaderValue(username, SecuredStringTests.build(password));
+ }
+}
diff --git a/src/test/java/org/elasticsearch/shield/authz/PrivilegeTests.java b/src/test/java/org/elasticsearch/shield/authz/PrivilegeTests.java
index 8b586f05a80..9f5f31e3c03 100644
--- a/src/test/java/org/elasticsearch/shield/authz/PrivilegeTests.java
+++ b/src/test/java/org/elasticsearch/shield/authz/PrivilegeTests.java
@@ -6,6 +6,9 @@
package org.elasticsearch.shield.authz;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
+import org.elasticsearch.action.get.GetAction;
+import org.elasticsearch.action.search.SearchAction;
+import org.elasticsearch.action.suggest.SuggestAction;
import org.elasticsearch.common.base.Predicate;
import org.elasticsearch.shield.support.AutomatonPredicate;
import org.elasticsearch.shield.support.Automatons;
@@ -176,4 +179,15 @@ public class PrivilegeTests extends ElasticsearchTestCase {
assertThat(predicate.apply("cluster:whatever"), is(false));
assertThat(predicate.apply("whatever"), is(false));
}
+
+ @Test
+ public void testSearchPrivilege() throws Exception {
+ Predicate predicate = Privilege.Index.SEARCH.predicate();
+ assertThat(predicate.apply(SearchAction.NAME), is(true));
+ assertThat(predicate.apply(SearchAction.NAME + "/whatever"), is(true));
+ assertThat(predicate.apply(GetAction.NAME), is(true));
+ assertThat(predicate.apply(GetAction.NAME + "/whatever"), is(true));
+ assertThat(predicate.apply(SuggestAction.NAME), is(true));
+ assertThat(predicate.apply(SuggestAction.NAME + "/whatever"), is(true));
+ }
}