| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.ignite.console.agent.rest; |
| |
| import java.io.IOException; |
| import java.net.InetSocketAddress; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.UUID; |
| import javax.net.ssl.SSLException; |
| import javax.net.ssl.SSLHandshakeException; |
| import com.fasterxml.jackson.databind.JsonNode; |
| import com.fasterxml.jackson.databind.ObjectMapper; |
| import org.apache.ignite.Ignite; |
| import org.apache.ignite.Ignition; |
| import org.apache.ignite.configuration.CacheConfiguration; |
| import org.apache.ignite.configuration.ConnectorConfiguration; |
| import org.apache.ignite.configuration.IgniteConfiguration; |
| import org.apache.ignite.internal.processors.rest.protocols.http.jetty.GridJettyObjectMapper; |
| import org.apache.ignite.internal.util.IgniteUtils; |
| import org.apache.ignite.internal.util.typedef.F; |
| import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; |
| import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; |
| import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; |
| import org.junit.Assert; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.ExpectedException; |
| |
| /** |
| * Test for RestExecutor. |
| */ |
| public class RestExecutorSelfTest { |
| /** Name of the cache created by default in the cluster. */ |
| private static final String DEFAULT_CACHE_NAME = "default"; |
| |
| /** Path to certificates and configs. */ |
| private static final String PATH_TO_RESOURCES = "modules/web-console/web-agent/src/test/resources/"; |
| |
| /** JSON object mapper. */ |
| private static final ObjectMapper MAPPER = new GridJettyObjectMapper(); |
| |
| /** */ |
| private static final String HTTP_URI = "http://localhost:8080"; |
| |
| /** */ |
| private static final String HTTPS_URI = "https://localhost:8080"; |
| |
| /** */ |
| private static final String JETTY_WITH_SSL = "jetty-with-ssl.xml"; |
| |
| /** */ |
| private static final String JETTY_WITH_CIPHERS_0 = "jetty-with-ciphers-0.xml"; |
| |
| /** */ |
| private static final String JETTY_WITH_CIPHERS_1 = "jetty-with-ciphers-1.xml"; |
| |
| /** */ |
| private static final String JETTY_WITH_CIPHERS_2 = "jetty-with-ciphers-2.xml"; |
| |
| /** This cipher is disabled by default in JDK 8. */ |
| private static final List<String> CIPHER_0 = Collections.singletonList("TLS_DH_anon_WITH_AES_256_GCM_SHA384"); |
| |
| /** */ |
| private static final List<String> CIPHER_1 = Collections.singletonList("TLS_RSA_WITH_NULL_SHA256"); |
| |
| /** */ |
| private static final List<String> CIPHER_2 = Collections.singletonList("TLS_ECDHE_ECDSA_WITH_NULL_SHA"); |
| |
| /** */ |
| private static final List<String> COMMON_CIPHERS = Arrays.asList( |
| "TLS_RSA_WITH_NULL_SHA256", |
| "TLS_ECDHE_ECDSA_WITH_NULL_SHA" |
| ); |
| |
| /** */ |
| @Rule |
| public final ExpectedException ruleForExpectedException = ExpectedException.none(); |
| |
| /** |
| * @param jettyCfg Optional path to file with Jetty XML config. |
| * @return Prepare configuration for cluster node. |
| */ |
| private IgniteConfiguration nodeConfiguration(String jettyCfg) { |
| TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(); |
| |
| ipFinder.registerAddresses(Collections.singletonList(new InetSocketAddress("127.0.0.1", 47500))); |
| |
| TcpDiscoverySpi discoverySpi = new TcpDiscoverySpi(); |
| |
| discoverySpi.setIpFinder(ipFinder); |
| |
| IgniteConfiguration cfg = new IgniteConfiguration(); |
| |
| cfg.setDiscoverySpi(discoverySpi); |
| |
| CacheConfiguration<Integer, String> dfltCacheCfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME); |
| |
| cfg.setCacheConfiguration(dfltCacheCfg); |
| |
| cfg.setIgniteInstanceName(UUID.randomUUID().toString()); |
| |
| if (!F.isEmpty(jettyCfg)) { |
| ConnectorConfiguration conCfg = new ConnectorConfiguration(); |
| conCfg.setJettyPath(resolvePath(jettyCfg)); |
| |
| cfg.setConnectorConfiguration(conCfg); |
| } |
| |
| return cfg; |
| } |
| |
| /** |
| * Convert response to JSON. |
| * |
| * @param res REST result. |
| * @return JSON object. |
| * @throws IOException If failed to parse. |
| */ |
| private JsonNode toJson(RestResult res) throws IOException { |
| Assert.assertNotNull(res); |
| |
| String data = res.getData(); |
| |
| Assert.assertNotNull(data); |
| Assert.assertFalse(data.isEmpty()); |
| |
| return MAPPER.readTree(data); |
| } |
| |
| /** |
| * @param file File name. |
| * @return Path to file. |
| */ |
| private String resolvePath(String file) { |
| return IgniteUtils.resolveIgnitePath(PATH_TO_RESOURCES + file).getAbsolutePath(); |
| } |
| |
| /** |
| * Try to execute REST command and check response. |
| * |
| * @param nodeCfg Node configuration. |
| * @param uri Node URI. |
| * @param keyStore Key store. |
| * @param keyStorePwd Key store password. |
| * @param trustStore Trust store. |
| * @param trustStorePwd Trust store password. |
| * @param cipherSuites Cipher suites. |
| * @throws Exception If failed. |
| */ |
| private void checkRest( |
| IgniteConfiguration nodeCfg, |
| String uri, |
| String keyStore, |
| String keyStorePwd, |
| String trustStore, |
| String trustStorePwd, |
| List<String> cipherSuites |
| ) throws Exception { |
| try ( |
| Ignite ignite = Ignition.getOrStart(nodeCfg); |
| RestExecutor exec = new RestExecutor(false, keyStore, keyStorePwd, trustStore, trustStorePwd, cipherSuites) |
| ) { |
| Map<String, Object> params = new HashMap<>(); |
| params.put("cmd", "top"); |
| params.put("attr", false); |
| params.put("mtr", false); |
| params.put("caches", false); |
| |
| RestResult res = exec.sendRequest(Collections.singletonList(uri), params, null); |
| |
| JsonNode json = toJson(res); |
| |
| Assert.assertTrue(json.isArray()); |
| |
| for (JsonNode item : json) { |
| Assert.assertTrue(item.get("attributes").isNull()); |
| Assert.assertTrue(item.get("metrics").isNull()); |
| Assert.assertTrue(item.get("caches").isNull()); |
| } |
| } |
| } |
| |
| /** */ |
| @Test |
| public void nodeNoSslAgentNoSsl() throws Exception { |
| checkRest( |
| nodeConfiguration(""), |
| HTTP_URI, |
| null, null, |
| null, null, |
| null |
| ); |
| } |
| |
| /** */ |
| @Test |
| public void nodeNoSslAgentWithSsl() throws Exception { |
| // Check Web Agent with SSL. |
| ruleForExpectedException.expect(SSLException.class); |
| checkRest( |
| nodeConfiguration(""), |
| HTTPS_URI, |
| resolvePath("client.jks"), "123456", |
| resolvePath("ca.jks"), "123456", |
| null |
| ); |
| } |
| |
| /** */ |
| @Test |
| public void nodeWithSslAgentNoSsl() throws Exception { |
| ruleForExpectedException.expect(IOException.class); |
| checkRest( |
| nodeConfiguration(JETTY_WITH_SSL), |
| HTTP_URI, |
| null, null, |
| null, null, |
| null |
| ); |
| } |
| |
| /** */ |
| @Test |
| public void nodeWithSslAgentWithSsl() throws Exception { |
| checkRest( |
| nodeConfiguration(JETTY_WITH_SSL), |
| HTTPS_URI, |
| resolvePath("client.jks"), "123456", |
| resolvePath("ca.jks"), "123456", |
| null |
| ); |
| } |
| |
| /** */ |
| @Test |
| public void nodeNoCiphersAgentWithCiphers() throws Exception { |
| ruleForExpectedException.expect(SSLHandshakeException.class); |
| checkRest( |
| nodeConfiguration(JETTY_WITH_SSL), |
| HTTPS_URI, |
| resolvePath("client.jks"), "123456", |
| resolvePath("ca.jks"), "123456", |
| CIPHER_0 |
| ); |
| } |
| |
| /** */ |
| @Test |
| public void nodeWithCiphersAgentNoCiphers() throws Exception { |
| ruleForExpectedException.expect(SSLHandshakeException.class); |
| checkRest( |
| nodeConfiguration(JETTY_WITH_CIPHERS_0), |
| HTTPS_URI, |
| resolvePath("client.jks"), "123456", |
| resolvePath("ca.jks"), "123456", |
| null |
| ); |
| } |
| |
| /** */ |
| @Test |
| public void nodeWithCiphersAgentWithCiphers() throws Exception { |
| checkRest( |
| nodeConfiguration(JETTY_WITH_CIPHERS_1), |
| HTTPS_URI, |
| resolvePath("client.jks"), "123456", |
| resolvePath("ca.jks"), "123456", |
| CIPHER_1 |
| ); |
| } |
| |
| /** */ |
| @Test |
| public void differentCiphers1() throws Exception { |
| ruleForExpectedException.expect(SSLHandshakeException.class); |
| checkRest( |
| nodeConfiguration(JETTY_WITH_CIPHERS_1), |
| HTTPS_URI, |
| resolvePath("client.jks"), "123456", |
| resolvePath("ca.jks"), "123456", |
| CIPHER_2 |
| ); |
| } |
| |
| /** */ |
| @Test |
| public void differentCiphers2() throws Exception { |
| ruleForExpectedException.expect(SSLException.class); |
| checkRest( |
| nodeConfiguration(JETTY_WITH_CIPHERS_2), |
| HTTPS_URI, |
| resolvePath("client.jks"), "123456", |
| resolvePath("ca.jks"), "123456", |
| CIPHER_1 |
| ); |
| } |
| |
| /** */ |
| @Test |
| public void commonCiphers() throws Exception { |
| checkRest( |
| nodeConfiguration(JETTY_WITH_CIPHERS_1), |
| HTTPS_URI, |
| resolvePath("client.jks"), "123456", |
| resolvePath("ca.jks"), "123456", |
| COMMON_CIPHERS |
| ); |
| } |
| } |