com.floragunn.searchguard.AbstractUnitTest.java Source code

Java tutorial

Introduction

Here is the source code for com.floragunn.searchguard.AbstractUnitTest.java

Source

/*
 * Copyright 2015 floragunn UG (haftungsbeschrnkt)
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.floragunn.searchguard;

import io.netty.handler.ssl.OpenSsl;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

import javax.net.ssl.SSLContext;
import javax.xml.bind.DatatypeConverter;

import junit.framework.Assert;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.elasticsearch.ElasticsearchTimeoutException;
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.NodesInfoRequest;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.PluginAwareNode;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestName;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;

import com.floragunn.searchguard.ssl.SearchGuardSSLPlugin;
import com.google.common.base.Strings;

public abstract class AbstractUnitTest {

    static {

        System.out.println("OS: " + System.getProperty("os.name") + " " + System.getProperty("os.arch") + " "
                + System.getProperty("os.version"));
        System.out.println(
                "Java Version: " + System.getProperty("java.version") + " " + System.getProperty("java.vendor"));
        System.out.println("JVM Impl.: " + System.getProperty("java.vm.version") + " "
                + System.getProperty("java.vm.vendor") + " " + System.getProperty("java.vm.name"));
        System.out.println("Open SSL available: " + OpenSsl.isAvailable());
        System.out.println("Open SSL version: " + OpenSsl.versionString());
    }

    @Rule
    public TestName name = new TestName();
    protected final String clustername = "searchguard_ssl_testcluster";

    protected Node esNode1;
    private Node esNode2;
    private Node esNode3;
    private String httpHost = null;
    private int httpPort = -1;
    protected Set<InetSocketTransportAddress> httpAdresses = new HashSet<InetSocketTransportAddress>();
    protected String nodeHost;
    protected int nodePort;
    protected boolean enableHTTPClientSSL = false;
    protected boolean enableHTTPClientSSLv3Only = false;
    protected boolean sendHTTPClientCertificate = false;
    protected boolean trustHTTPServerCertificate = false;
    protected String keystore = "node-0-keystore.jks";

    @Rule
    public final TestWatcher testWatcher = new TestWatcher() {
        @Override
        protected void starting(final Description description) {
            final String methodName = description.getMethodName();
            String className = description.getClassName();
            className = className.substring(className.lastIndexOf('.') + 1);
            System.out.println(
                    "---------------- Starting JUnit-test: " + className + " " + methodName + " ----------------");
        }

        @Override
        protected void failed(final Throwable e, final Description description) {
            final String methodName = description.getMethodName();
            String className = description.getClassName();
            className = className.substring(className.lastIndexOf('.') + 1);
            System.out.println(">>>> " + className + " " + methodName + " FAILED due to " + e);
        }

        @Override
        protected void finished(final Description description) {
            // System.out.println("-----------------------------------------------------------------------------------------");
        }

    };

    protected AbstractUnitTest() {
        super();
    }

    // @formatter:off
    private Settings.Builder getDefaultSettingsBuilder(final int nodenum, final boolean dataNode,
            final boolean masterNode) {

        return Settings.settingsBuilder().put("node.name", "searchguard_testnode_" + nodenum)
                .put("node.data", dataNode).put("node.master", masterNode).put("cluster.name", clustername)
                .put("path.data", "data/data").put("path.work", "data/work").put("path.logs", "data/logs")
                .put("path.conf", "data/config").put("path.plugins", "data/plugins")
                .put("index.number_of_shards", "1").put("index.number_of_replicas", "0").put("http.enabled", true)
                .put("cluster.routing.allocation.disk.watermark.high", "1mb")
                .put("cluster.routing.allocation.disk.watermark.low", "1mb").put("http.cors.enabled", true)
                .put("node.local", false).put("discovery.zen.minimum_master_nodes", 1).put("path.home", ".");
    }
    // @formatter:on

    protected final ESLogger log = Loggers.getLogger(this.getClass());

    protected final String getHttpServerUri() {
        final String address = "http" + (enableHTTPClientSSL ? "s" : "") + "://" + httpHost + ":" + httpPort;
        log.debug("Connect to {}", address);
        return address;
    }

    public final void startES(final Settings settings) throws Exception {
        startES(settings, 30, 3);
    }

    public final void startES(final Settings settings, int timeOutSec, int assertNodes) throws Exception {

        FileUtils.deleteDirectory(new File("data"));

        esNode1 = new PluginAwareNode(
                getDefaultSettingsBuilder(1, false, true)
                        .put(settings == null ? Settings.Builder.EMPTY_SETTINGS : settings).build(),
                SearchGuardSSLPlugin.class, SearchGuardPlugin.class);
        esNode2 = new PluginAwareNode(
                getDefaultSettingsBuilder(2, true, true)
                        .put(settings == null ? Settings.Builder.EMPTY_SETTINGS : settings).build(),
                SearchGuardSSLPlugin.class, SearchGuardPlugin.class);
        esNode3 = new PluginAwareNode(
                getDefaultSettingsBuilder(3, true, false)
                        .put(settings == null ? Settings.Builder.EMPTY_SETTINGS : settings).build(),
                SearchGuardSSLPlugin.class, SearchGuardPlugin.class);

        esNode1.start();
        esNode2.start();
        esNode3.start();

        waitForCluster(ClusterHealthStatus.GREEN, TimeValue.timeValueSeconds(timeOutSec), esNode1.client(),
                assertNodes);
    }

    protected Client client() {
        return esNode1.client();
    }

    @Before
    public void setUp() throws Exception {
        enableHTTPClientSSL = false;
        enableHTTPClientSSLv3Only = false;
    }

    @After
    public void tearDown() throws Exception {

        Thread.sleep(500);
        if (esNode3 != null) {
            esNode3.close();
        }

        if (esNode2 != null) {
            esNode2.close();
        }

        if (esNode1 != null) {
            esNode1.close();
        }
    }

    protected void waitForGreenClusterState(final Client client) throws IOException {
        waitForCluster(ClusterHealthStatus.GREEN, TimeValue.timeValueSeconds(30), client, 3);
    }

    protected void waitForCluster(final ClusterHealthStatus status, final TimeValue timeout, final Client client,
            int assertNodes) throws IOException {
        try {
            log.debug("waiting for cluster state {}", status.name());
            final ClusterHealthResponse healthResponse = client.admin().cluster().prepareHealth()
                    .setWaitForStatus(status).setTimeout(timeout).setWaitForNodes(String.valueOf(assertNodes))
                    .execute().actionGet();
            if (healthResponse.isTimedOut()) {
                throw new IOException("cluster state is " + healthResponse.getStatus().name() + " with "
                        + healthResponse.getNumberOfNodes() + " nodes");
            } else {
                log.debug("... cluster state ok " + healthResponse.getStatus().name() + " with "
                        + healthResponse.getNumberOfNodes() + " nodes");
            }

            org.junit.Assert.assertEquals(assertNodes, healthResponse.getNumberOfNodes());

            final NodesInfoResponse res = esNode1.client().admin().cluster().nodesInfo(new NodesInfoRequest())
                    .actionGet();

            final NodeInfo[] nodes = res.getNodes();

            for (int i = 0; i < nodes.length; i++) {
                final NodeInfo nodeInfo = nodes[i];
                if (nodeInfo.getHttp() != null && nodeInfo.getHttp().address() != null) {
                    final InetSocketTransportAddress is = (InetSocketTransportAddress) nodeInfo.getHttp().address()
                            .publishAddress();
                    httpPort = is.getPort();
                    httpHost = is.getHost();
                    httpAdresses.add(is);
                }

                final InetSocketTransportAddress is = (InetSocketTransportAddress) nodeInfo.getTransport()
                        .getAddress().publishAddress();
                nodePort = is.getPort();
                nodeHost = is.getHost();
            }
        } catch (final ElasticsearchTimeoutException e) {
            throw new IOException(
                    "timeout, cluster does not respond to health request, cowardly refusing to continue with operations");
        }
    }

    public File getAbsoluteFilePathFromClassPath(final String fileNameFromClasspath) {
        File file = null;
        final URL fileUrl = AbstractUnitTest.class.getClassLoader().getResource(fileNameFromClasspath);
        if (fileUrl != null) {
            try {
                file = new File(URLDecoder.decode(fileUrl.getFile(), "UTF-8"));
            } catch (final UnsupportedEncodingException e) {
                return null;
            }

            if (file.exists() && file.canRead()) {
                return file;
            } else {
                log.error("Cannot read from {}, maybe the file does not exists? ", file.getAbsolutePath());
            }

        } else {
            log.error("Failed to load " + fileNameFromClasspath);
        }
        return null;
    }

    protected String executeSimpleRequest(final String request) throws Exception {

        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
        try {
            httpClient = getHTTPClient();
            response = httpClient.execute(new HttpGet(getHttpServerUri() + "/" + request));

            if (response.getStatusLine().getStatusCode() >= 300) {
                throw new Exception("Statuscode " + response.getStatusLine().getStatusCode());
            }

            return IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
        } finally {

            if (response != null) {
                response.close();
            }

            if (httpClient != null) {
                httpClient.close();
            }
        }
    }

    protected class HttpResponse {
        private final CloseableHttpResponse inner;
        private final String body;
        private final Header[] header;
        private final int statusCode;
        private final String statusReason;

        public HttpResponse(CloseableHttpResponse inner) throws IllegalStateException, IOException {
            super();
            this.inner = inner;
            this.body = inner.getEntity() == null ? null
                    : IOUtils.toString(inner.getEntity().getContent(), StandardCharsets.UTF_8);
            this.header = inner.getAllHeaders();
            this.statusCode = inner.getStatusLine().getStatusCode();
            this.statusReason = inner.getStatusLine().getReasonPhrase();
            inner.close();
        }

        public CloseableHttpResponse getInner() {
            return inner;
        }

        public String getBody() {
            return body;
        }

        public Header[] getHeader() {
            return header;
        }

        public int getStatusCode() {
            return statusCode;
        }

        public String getStatusReason() {
            return statusReason;
        }

    }

    protected HttpResponse executeGetRequest(final String request, Header... header) throws Exception {
        return executeRequest(new HttpGet(getHttpServerUri() + "/" + request), header);
    }

    protected HttpResponse executeHeadRequest(final String request, Header... header) throws Exception {
        return executeRequest(new HttpHead(getHttpServerUri() + "/" + request), header);
    }

    protected HttpResponse executePutRequest(final String request, String body, Header... header) throws Exception {
        HttpPut uriRequest = new HttpPut(getHttpServerUri() + "/" + request);
        if (!Strings.isNullOrEmpty(body)) {
            uriRequest.setEntity(new StringEntity(body));
        }

        return executeRequest(uriRequest, header);

    }

    protected HttpResponse executePostRequest(final String request, String body, Header... header)
            throws Exception {
        HttpPost uriRequest = new HttpPost(getHttpServerUri() + "/" + request);
        if (!Strings.isNullOrEmpty(body)) {
            uriRequest.setEntity(new StringEntity(body));
        }

        return executeRequest(uriRequest, header);
    }

    protected HttpResponse executeDeleteRequest(final String request, Header... header) throws Exception {
        return executeRequest(new HttpDelete(getHttpServerUri() + "/" + request), header);
    }

    protected HttpResponse executeRequest(HttpUriRequest uriRequest, Header... header) throws Exception {

        CloseableHttpClient httpClient = null;
        try {

            httpClient = getHTTPClient();

            if (header != null && header.length > 0) {
                for (int i = 0; i < header.length; i++) {
                    Header h = header[i];
                    uriRequest.addHeader(h);
                }
            }

            HttpResponse res = new HttpResponse(httpClient.execute(uriRequest));
            log.trace(res.getBody());
            return res;
        } finally {

            if (httpClient != null) {
                httpClient.close();
            }
        }
    }

    protected final CloseableHttpClient getHTTPClient() throws Exception {

        final HttpClientBuilder hcb = HttpClients.custom();

        if (enableHTTPClientSSL) {

            log.debug("Configure HTTP client with SSL");

            final KeyStore myTrustStore = KeyStore.getInstance("JKS");
            myTrustStore.load(new FileInputStream(getAbsoluteFilePathFromClassPath("truststore.jks")),
                    "changeit".toCharArray());

            final KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(new FileInputStream(getAbsoluteFilePathFromClassPath(keystore)),
                    "changeit".toCharArray());

            final SSLContextBuilder sslContextbBuilder = SSLContexts.custom().useTLS();

            if (trustHTTPServerCertificate) {
                sslContextbBuilder.loadTrustMaterial(myTrustStore);
            }

            if (sendHTTPClientCertificate) {
                sslContextbBuilder.loadKeyMaterial(keyStore, "changeit".toCharArray());
            }

            final SSLContext sslContext = sslContextbBuilder.build();

            String[] protocols = null;

            if (enableHTTPClientSSLv3Only) {
                protocols = new String[] { "SSLv3" };
            } else {
                protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
            }

            final SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, protocols, null,
                    SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

            hcb.setSSLSocketFactory(sslsf);
        }

        hcb.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(60 * 1000).build());

        return hcb.build();
    }

    protected final String loadFile(final String file) throws IOException {
        final StringWriter sw = new StringWriter();
        IOUtils.copy(this.getClass().getResourceAsStream("/" + file), sw, StandardCharsets.UTF_8);
        return sw.toString();
    }

    protected BytesReference readYamlContent(final String file) {
        try {
            return readXContent(new StringReader(loadFile(file)), XContentType.YAML);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    protected BytesReference readXContent(final Reader reader, final XContentType xContentType) throws IOException {
        XContentParser parser = null;
        try {
            parser = XContentFactory.xContent(xContentType).createParser(reader);
            parser.nextToken();
            final XContentBuilder builder = XContentFactory.jsonBuilder();
            builder.copyCurrentStructure(parser);
            return builder.bytes();
        } finally {
            if (parser != null) {
                parser.close();
            }
        }
    }

    public static String encodeBasicHeader(final String username, final String password) {
        return new String(DatatypeConverter.printBase64Binary(
                (username + ":" + Objects.requireNonNull(password)).getBytes(StandardCharsets.UTF_8)));
    }
}