diff --git a/pom.xml b/pom.xml index c7eaff4560d..2b8c8271589 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,7 @@ 512m 5 .local-${project.version}-execution-hints.log + false @@ -119,6 +120,18 @@ 2.1 test + + org.apache.httpcomponents + httpclient + 4.3.5 + test + + + org.codehaus.groovy + groovy-all + 2.3.2 + test + diff --git a/src/test/java/org/elasticsearch/test/ShieldRestTests.java b/src/test/java/org/elasticsearch/test/ShieldRestTests.java new file mode 100644 index 00000000000..183c731d174 --- /dev/null +++ b/src/test/java/org/elasticsearch/test/ShieldRestTests.java @@ -0,0 +1,168 @@ +/* + * 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.test; + +import com.carrotsearch.randomizedtesting.SysGlobals; +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.google.common.base.Charsets; +import com.google.common.io.Files; +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.common.os.OsUtils; +import org.elasticsearch.common.settings.ImmutableSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.plugins.PluginsService; +import org.elasticsearch.shield.plugin.ShieldPlugin; +import org.elasticsearch.shield.transport.netty.NettySecuredTransport; +import org.elasticsearch.test.rest.ElasticsearchRestTests; +import org.elasticsearch.test.rest.RestTestCandidate; +import org.elasticsearch.transport.TransportModule; +import org.junit.AfterClass; +import org.junit.ClassRule; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; + +import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.basicAuthHeaderValue; +import static org.hamcrest.Matchers.is; + +/** + * + */ +public class ShieldRestTests extends ElasticsearchRestTests { + + public static final int CHILD_JVM_ID = Integer.parseInt(System.getProperty(SysGlobals.CHILDVM_SYSPROP_JVM_ID, "0")); + public static final int BASE_PORT = 33000 + CHILD_JVM_ID * 100; + public static final String BASE_PORT_RANGE = BASE_PORT + "-" + (BASE_PORT+10) ; + + protected static final boolean ENABLE_TRANSPORT_SSL = true; + protected static final boolean SHIELD_AUDIT_ENABLED = false; + + protected static final String DEFAULT_USER_NAME = "test_user"; + protected static final String DEFAULT_PASSWORD = "changeme"; + protected static final String DEFAULT_ROLE = "user"; + + public static final String CONFIG_IPFILTER_ALLOW_ALL = "allow: all\n"; + public static final String CONFIG_STANDARD_USER = DEFAULT_USER_NAME + ":{plain}" + DEFAULT_PASSWORD + "\n"; + public static final String CONFIG_STANDARD_USER_ROLES = DEFAULT_USER_NAME + ":" + DEFAULT_ROLE + "\n"; + public static final String CONFIG_ROLE_ALLOW_ALL = "user:\n" + + " cluster: ALL\n" + + " indices:\n" + + " '.*': ALL\n"; + + static { + InternalTestCluster.DEFAULT_SETTINGS_SOURCE = new SettingsSource() { + + @Override + public Settings node(int nodeOrdinal) { + File store; + try { + store = new File(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks").toURI()); + assertThat(store.exists(), is(true)); + } catch (Exception e) { + throw new ElasticsearchException("Error reading test node cert", e); + } + String password = "testnode"; + + File folder = createFolder(); + + ImmutableSettings.Builder builder = ImmutableSettings.builder() + .put("request.headers.Authorization", basicAuthHeaderValue(DEFAULT_USER_NAME, DEFAULT_PASSWORD.toCharArray())) + .put("discovery.zen.ping.multicast.enabled", false) + .put("discovery.type", "zen") + .put("node.mode", "network") + .put("plugin.types", ShieldPlugin.class.getName()) + .put("shield.authc.esusers.files.users", createFile(folder, "users", CONFIG_STANDARD_USER)) + .put("shield.authc.esusers.files.users_roles", createFile(folder, "users_roles", CONFIG_STANDARD_USER_ROLES)) + .put("shield.authz.store.files.roles", createFile(folder, "roles.yml", CONFIG_ROLE_ALLOW_ALL)) + .put("shield.transport.n2n.ip_filter.file", createFile(folder, "ip_filter.yml", CONFIG_IPFILTER_ALLOW_ALL)) + .put("shield.transport.ssl", ENABLE_TRANSPORT_SSL) + .put("shield.transport.ssl.keystore", store.getPath()) + .put("shield.transport.ssl.keystore_password", password) + .put("shield.transport.ssl.truststore", store.getPath()) + .put("shield.transport.ssl.truststore_password", password) + .put("shield.http.ssl", false) + .put("transport.tcp.port", BASE_PORT_RANGE) + .putArray("discovery.zen.ping.unicast.hosts", "127.0.0.1:" + BASE_PORT, "127.0.0.1:" + (BASE_PORT + 1), "127.0.0.1:" + (BASE_PORT + 2), "127.0.0.1:" + (BASE_PORT + 3)) + .put("shield.audit.enabled", SHIELD_AUDIT_ENABLED); + + builder.put("network.host", "127.0.0.1"); + if (OsUtils.MAC) { + builder.put("network.host", randomBoolean() ? "127.0.0.1" : "::1"); + } + + return builder.build(); + } + + @Override + public Settings transportClient() { + File store; + String password = "testclient"; + try { + store = new File(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks").toURI()); + assertThat(store.exists(), is(true)); + } catch (Exception e) { + throw new ElasticsearchException("Error reading test client cert", e); + } + + File folder = createFolder(); + + return ImmutableSettings.builder() + .put("request.headers.Authorization", basicAuthHeaderValue(DEFAULT_USER_NAME, DEFAULT_PASSWORD.toCharArray())) + .put(TransportModule.TRANSPORT_TYPE_KEY, NettySecuredTransport.class.getName()) + .put("plugins." + PluginsService.LOAD_PLUGIN_FROM_CLASSPATH, false) + .put("node.mode", "network") + .put("shield.transport.n2n.ip_filter.file", createFile(folder, "ip_filter.yml", CONFIG_IPFILTER_ALLOW_ALL)) + .put("shield.transport.ssl", ENABLE_TRANSPORT_SSL) + .put("shield.transport.ssl.keystore", store.getPath()) + .put("shield.transport.ssl.keystore_password", password) + .put("shield.transport.ssl.truststore", store.getPath()) + .put("shield.transport.ssl.truststore_password", password) + .put("cluster.name", internalCluster().getClusterName()) + .build(); + } + }; + } + + @ClassRule + public static TemporaryFolder tmpFolder = new TemporaryFolder(); + + @AfterClass + public static void cleanup() { + tmpFolder = null; + } + + public ShieldRestTests(@Name("yaml") RestTestCandidate testCandidate) { + super(testCandidate); + } + + @Override + protected Settings restClientSettings() { + return ImmutableSettings.builder() + .put("request.headers.Authorization", basicAuthHeaderValue(DEFAULT_USER_NAME, DEFAULT_PASSWORD.toCharArray())).build(); + } + + /* static helper methods for the global test class */ + static File createFolder() { + try { + return tmpFolder.newFolder(); + } catch (IOException ioe) { + fail("could not create temporary folder"); + return null; + } + } + + static String createFile(File folder, String name, String content) { + Path file = folder.toPath().resolve(name); + try { + Files.write(content.getBytes(Charsets.UTF_8), file.toFile()); + } catch (IOException e) { + throw new ElasticsearchException("Error writing file in test", e); + } + return file.toFile().getAbsolutePath(); + } +}