/*
* 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.internal.client;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.X509TrustManager;
import org.apache.ignite.configuration.ConnectorConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.client.balancer.GridClientRoundRobinBalancer;
import org.apache.ignite.internal.client.impl.GridClientImpl;
import org.apache.ignite.internal.client.ssl.GridSslBasicContextFactory;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.testframework.GridTestUtils;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
/**
* Tests
*/
public class ClientTcpSslAuthenticationSelfTest extends GridCommonAbstractTest {
/** REST TCP port. */
private static final int REST_TCP_PORT = 12121;
/** Test trust manager for server. */
private MockX509TrustManager srvTrustMgr = new MockX509TrustManager();
/** Test trust manager for client. */
private MockX509TrustManager clientTrustMgr = new MockX509TrustManager();
/** Whether server should check clients. */
private volatile boolean checkClient;
/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
assertEquals(0, srvTrustMgr.serverCheckCallCount());
assertEquals(0, clientTrustMgr.clientCheckCallCount());
}
/** {@inheritDoc} */
@Override protected void beforeTest() throws Exception {
srvTrustMgr.reset();
clientTrustMgr.reset();
}
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
IgniteConfiguration c = super.getConfiguration(igniteInstanceName);
c.setLocalHost(getTestResources().getLocalHost());
assert c.getConnectorConfiguration() == null;
ConnectorConfiguration clientCfg = new ConnectorConfiguration();
clientCfg.setPort(REST_TCP_PORT);
clientCfg.setSslEnabled(true);
clientCfg.setSslClientAuth(checkClient);
clientCfg.setSslClientAuth(checkClient);
GridSslBasicContextFactory factory = (GridSslBasicContextFactory)GridTestUtils.sslContextFactory();
factory.setTrustManagers(srvTrustMgr);
clientCfg.setSslContextFactory(factory);
c.setConnectorConfiguration(clientCfg);
return c;
}
/**
* Creates client that will try to connect to only first node in grid.
*
* @return Client.
* @throws Exception If failed to create client.
*/
private GridClientImpl createClient() throws Exception {
GridClientConfiguration cfg = new GridClientConfiguration();
cfg.setServers(Arrays.asList(U.getLocalHost().getHostAddress() + ":" + REST_TCP_PORT));
cfg.setBalancer(new GridClientRoundRobinBalancer());
GridSslBasicContextFactory factory = (GridSslBasicContextFactory)GridTestUtils.sslContextFactory();
factory.setTrustManagers(clientTrustMgr);
cfg.setSslContextFactory(factory);
return (GridClientImpl)GridClientFactory.start(cfg);
}
/**
* @throws Exception If failed.
*/
public void testServerAuthenticated() throws Exception {
checkServerAuthenticatedByClient(false);
}
/**
* @throws Exception If failed.
*/
public void testServerNotAuthenticatedByClient() throws Exception {
try {
checkServerAuthenticatedByClient(true);
}
catch (GridClientDisconnectedException e) {
assertTrue(X.hasCause(e, GridServerUnreachableException.class));
}
}
/**
* @throws Exception If failed.
*/
public void testClientAuthenticated() throws Exception {
checkClientAuthenticatedByServer(false);
}
/**
* @throws Exception If failed.
*/
public void testClientNotAuthenticated() throws Exception {
try {
checkServerAuthenticatedByClient(true);
}
catch (GridClientDisconnectedException e) {
assertTrue(X.hasCause(e, GridServerUnreachableException.class));
}
}
/**
* @param fail Should client trust manager fail.
* @throws Exception If failed.
*/
private void checkServerAuthenticatedByClient(boolean fail) throws Exception {
checkClient = false;
srvTrustMgr.shouldFail(false);
clientTrustMgr.shouldFail(fail);
startGrid();
try {
try (GridClientImpl c = createClient()) {
c.compute().refreshTopology(false, false);
}
}
finally {
G.stopAll(false);
}
assertEquals(0, srvTrustMgr.clientCheckCallCount());
assertEquals(1, clientTrustMgr.serverCheckCallCount());
}
/**
* @param fail Should server trust manager fail.
* @throws Exception If failed.
*/
private void checkClientAuthenticatedByServer(boolean fail) throws Exception {
checkClient = true;
srvTrustMgr.shouldFail(fail);
clientTrustMgr.shouldFail(false);
startGrid();
try {
try (GridClientImpl c = createClient()) {
c.compute().refreshTopology(false, false);
}
}
finally {
G.stopAll(false);
}
assertEquals(1, srvTrustMgr.clientCheckCallCount());
assertEquals(1, clientTrustMgr.serverCheckCallCount());
}
/**
* Test trust manager to emulate certificate check failures.
*/
private static class MockX509TrustManager implements X509TrustManager {
/** Empty array. */
private static final X509Certificate[] EMPTY = new X509Certificate[0];
/** Whether checks should fail. */
private volatile boolean shouldFail;
/** Client check call count. */
private AtomicInteger clientCheckCallCnt = new AtomicInteger();
/** Server check call count. */
private AtomicInteger srvCheckCallCnt = new AtomicInteger();
/**
* @param shouldFail Whether checks should fail.
*/
private void shouldFail(boolean shouldFail) {
this.shouldFail = shouldFail;
}
/** {@inheritDoc} */
@Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
throws CertificateException {
clientCheckCallCnt.incrementAndGet();
if (shouldFail)
throw new CertificateException("Client check failed.");
}
/** {@inheritDoc} */
@Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
throws CertificateException {
srvCheckCallCnt.incrementAndGet();
if (shouldFail)
throw new CertificateException("Server check failed.");
}
/** {@inheritDoc} */
@Override public X509Certificate[] getAcceptedIssuers() {
return EMPTY;
}
/**
* @return Call count to checkClientTrusted method.
*/
public int clientCheckCallCount() {
return clientCheckCallCnt.get();
}
/**
* @return Call count to checkServerTrusted method.
*/
public int serverCheckCallCount() {
return srvCheckCallCnt.get();
}
/**
* Clears should fail flag and resets call counters.
*/
public void reset() {
shouldFail = false;
clientCheckCallCnt.set(0);
srvCheckCallCnt.set(0);
}
}
}