diff --git a/qa/pom.xml b/qa/pom.xml
index 3444a8d3f7c..a2e3b1d0dbf 100644
--- a/qa/pom.xml
+++ b/qa/pom.xml
@@ -321,6 +321,7 @@
smoke-test-watcher-with-shield
shield-example-realm
shield-tribe-node-tests
+ shield-client-tests
diff --git a/qa/shield-client-tests/integration-tests.xml b/qa/shield-client-tests/integration-tests.xml
new file mode 100644
index 00000000000..56f8290b01c
--- /dev/null
+++ b/qa/shield-client-tests/integration-tests.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Waiting for elasticsearch to become available on port @{port}...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Adding shield users...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Checking we can connect with basic auth on port ${integ.http.port}...
+
+
+
+
+
diff --git a/qa/shield-client-tests/pom.xml b/qa/shield-client-tests/pom.xml
new file mode 100644
index 00000000000..a8a6240010d
--- /dev/null
+++ b/qa/shield-client-tests/pom.xml
@@ -0,0 +1,144 @@
+
+
+
+ x-plugins-qa
+ org.elasticsearch.qa
+ 3.0.0-SNAPSHOT
+
+ 4.0.0
+
+ shield-client-tests
+ QA: Shield transport client tests
+ Run tests with a Transport Client for communication with a Shield enabled cluster
+
+
+ true
+ false
+ ${project.basedir}/integration-tests.xml
+ license,shield
+
+
+
+
+ org.elasticsearch.plugin
+ shield
+ ${elasticsearch.version}
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ integ-setup-dependencies
+ pre-integration-test
+
+ copy
+
+
+ ${skip.integ.tests}
+ true
+ ${integ.deps}/plugins
+
+
+
+
+ org.elasticsearch.distribution.zip
+ elasticsearch
+ ${elasticsearch.version}
+ zip
+ true
+ ${integ.deps}
+
+
+
+
+ org.elasticsearch.plugin
+ license
+ ${elasticsearch.version}
+ zip
+ true
+
+
+
+ org.elasticsearch.plugin
+ shield
+ ${elasticsearch.version}
+ zip
+ true
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+
+ integ-setup
+ pre-integration-test
+
+ run
+
+
+
+
+
+
+
+
+
+ ${skip.integ.tests}
+
+
+
+
+ integ-teardown
+ post-integration-test
+
+ run
+
+
+
+
+
+ ${skip.integ.tests}
+
+
+
+
+
+ ant-contrib
+ ant-contrib
+ 1.0b3
+
+
+ ant
+ ant
+
+
+
+
+ org.apache.ant
+ ant-nodeps
+ 1.8.1
+
+
+
+
+
+
diff --git a/qa/shield-client-tests/src/test/java/org/elasticsearch/shield/qa/ShieldTransportClientIT.java b/qa/shield-client-tests/src/test/java/org/elasticsearch/shield/qa/ShieldTransportClientIT.java
new file mode 100644
index 00000000000..e2c6ce6adc9
--- /dev/null
+++ b/qa/shield-client-tests/src/test/java/org/elasticsearch/shield/qa/ShieldTransportClientIT.java
@@ -0,0 +1,120 @@
+/*
+ * 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.qa;
+
+import org.elasticsearch.ElasticsearchSecurityException;
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
+import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
+import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
+import org.elasticsearch.client.transport.TransportClient;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.transport.TransportAddress;
+import org.elasticsearch.plugins.Plugin;
+import org.elasticsearch.shield.ShieldPlugin;
+import org.elasticsearch.shield.authc.support.SecuredString;
+import org.elasticsearch.test.ESIntegTestCase;
+import org.junit.Test;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
+import static org.hamcrest.Matchers.*;
+
+/**
+ * Integration tests that test a transport client with Shield being loaded that connect to an external cluster
+ */
+public class ShieldTransportClientIT extends ESIntegTestCase {
+
+ static final String ADMIN_USER_PW = "test_user:changeme";
+ static final String TRANSPORT_USER_PW = "transport:changeme";
+
+ @Override
+ protected Settings externalClusterClientSettings() {
+ return Settings.builder()
+ .put("shield.user", ADMIN_USER_PW)
+ .build();
+ }
+
+ @Override
+ protected Collection> transportClientPlugins() {
+ return Collections.singletonList(ShieldPlugin.class);
+ }
+
+ @Test
+ public void testThatTransportClientWithoutAuthenticationDoesNotWork() throws Exception {
+ try (TransportClient client = transportClient(Settings.EMPTY)) {
+ boolean connected = awaitBusy(() -> {
+ return client.connectedNodes().size() > 0;
+ }, 5L, TimeUnit.SECONDS);
+
+ assertThat(connected, is(false));
+ }
+ }
+
+ @Test
+ public void testThatTransportClientAuthenticationWithTransportClientRole() throws Exception {
+ Settings settings = Settings.builder()
+ .put("shield.user", TRANSPORT_USER_PW)
+ .build();
+ try (TransportClient client = transportClient(settings)) {
+ boolean connected = awaitBusy(() -> {
+ return client.connectedNodes().size() > 0;
+ }, 5L, TimeUnit.SECONDS);
+
+ assertThat(connected, is(true));
+
+ // this checks that the transport client is really running in a limited state
+ try {
+ client.admin().cluster().prepareHealth().get();
+ fail("the transport user should not be be able to get health!");
+ } catch (ElasticsearchSecurityException e) {
+ assertThat(e.toString(), containsString("unauthorized"));
+ }
+ }
+ }
+
+ @Test
+ public void testTransportClientWithAdminUser() throws Exception {
+ final boolean useTransportUser = randomBoolean();
+ Settings settings = Settings.builder()
+ .put("shield.user", useTransportUser ? TRANSPORT_USER_PW : ADMIN_USER_PW)
+ .build();
+ try (TransportClient client = transportClient(settings)) {
+ boolean connected = awaitBusy(() -> {
+ return client.connectedNodes().size() > 0;
+ }, 5L, TimeUnit.SECONDS);
+
+ assertThat(connected, is(true));
+
+ // this checks that the transport client is really running in a limited state
+ ClusterHealthResponse response;
+ if (useTransportUser) {
+ response = client.admin().cluster().prepareHealth().putHeader("Authorization", basicAuthHeaderValue("test_user", new SecuredString("changeme".toCharArray()))).get();
+ } else {
+ response = client.admin().cluster().prepareHealth().get();
+ }
+
+ assertThat(response.isTimedOut(), is(false));
+ }
+ }
+
+ TransportClient transportClient(Settings extraSettings) {
+ NodesInfoResponse nodeInfos = client().admin().cluster().prepareNodesInfo().get();
+ NodeInfo[] nodes = nodeInfos.getNodes();
+ assertTrue(nodes.length > 0);
+ TransportAddress publishAddress = randomFrom(nodes).getTransport().address().publishAddress();
+ String clusterName = nodeInfos.getClusterNameAsString();
+
+ Settings settings = Settings.builder()
+ .put(extraSettings)
+ .put("cluster.name", clusterName)
+ .build();
+
+ return TransportClient.builder().settings(settings).addPlugin(ShieldPlugin.class).build().addTransportAddress(publishAddress);
+ }
+}
diff --git a/shield/config/shield/roles.yml b/shield/config/shield/roles.yml
index 406931d4ea4..d35711cf9bf 100644
--- a/shield/config/shield/roles.yml
+++ b/shield/config/shield/roles.yml
@@ -21,18 +21,9 @@ user:
# Defines the required permissions for transport clients
transport_client:
cluster:
- - cluster:monitor/nodes/info
- #uncomment the following for sniffing
- #- cluster:monitor/state
-
-# The required role for kibana 3 users
-kibana3:
- cluster: cluster:monitor/nodes/info
- indices:
- '*':
- privileges: indices:data/read/search, indices:data/read/get, indices:admin/get
- 'kibana-int':
- privileges: indices:data/read/search, indices:data/read/get, indices:data/write/delete, indices:data/write/index, create_index
+ - cluster:monitor/nodes/liveness
+ #uncomment the following for sniffing
+ #- cluster:monitor/state
# The required permissions for kibana 4 users.
kibana4:
@@ -60,18 +51,3 @@ logstash:
indices:
'logstash-*':
privileges: indices:data/write/bulk, indices:data/write/delete, indices:data/write/update, indices:data/read/search, indices:data/read/scroll, create_index
-
-# Marvel role, allowing all operations
-# on the marvel indices
-marvel_user:
- cluster: cluster:monitor/nodes/info, cluster:admin/plugin/license/get
- indices:
- '.marvel-*':
- privileges: all
-
-# Marvel Agent users
-marvel_agent:
- cluster: indices:admin/template/get, indices:admin/template/put
- indices:
- '.marvel-*':
- privileges: indices:data/write/bulk, create_index
diff --git a/shield/docs/public/release-notes.asciidoc b/shield/docs/public/release-notes.asciidoc
index 652e4bfcb1f..f633edcb977 100644
--- a/shield/docs/public/release-notes.asciidoc
+++ b/shield/docs/public/release-notes.asciidoc
@@ -34,7 +34,7 @@ The default role definitions in the `roles.yml` file may need to be changed to e
applications such as Marvel and Kibana. Any role changes will be found in `roles.yml.new` after upgrading to the new
version of Shield. We recommend copying the changes listed below to your `roles.yml` file.
-* added[2.0.0-beta2] The permission on all the roles are updated to the verbose format to make it easer to enable field level and document level security.
+* added[2.0.0-beta2] The permission on all the roles are updated to the verbose format to make it easier to enable field level and document level security. `transport_client` role updated to work with Elasticsearch 2.0.0-beta2. `kibana3`, `marvel_user`, and `marvel_agent` roles removed.
* added[1.1.0] `kibana4_server` role added that defines the minimum set of permissions necessary for the Kibana 4 server.
* added[1.0.1] `kibana4` role updated to work with new features in Kibana 4 RC1