You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by il...@apache.org on 2020/03/16 10:07:34 UTC
[ignite] branch master updated: IGNITE-12752 Pass client
certificates to security - Fixes #7508.
This is an automated email from the ASF dual-hosted git repository.
ilyak pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 19ffc88 IGNITE-12752 Pass client certificates to security - Fixes #7508.
19ffc88 is described below
commit 19ffc883d7cb25c0b998f9437cd6a927ff477b1f
Author: Ilya Kasnacheev <il...@gmail.com>
AuthorDate: Mon Mar 16 13:06:48 2020 +0300
IGNITE-12752 Pass client certificates to security - Fixes #7508.
Signed-off-by: Ilya Kasnacheev <il...@gmail.com>
---
.../rest/JettyRestProcessorCommonSelfTest.java | 25 ++-
.../rest/protocols/tcp/TcpRestParserSelfTest.java | 1 +
modules/clients/src/test/keystore/node0102.jks | Bin 0 -> 4618 bytes
.../resources/jetty/rest-jetty-ssl-client-auth.xml | 92 ++++++++
.../ClientListenerAbstractConnectionContext.java | 8 +-
.../odbc/ClientListenerConnectionContext.java | 5 +-
.../processors/odbc/ClientListenerNioListener.java | 10 +-
.../odbc/jdbc/JdbcConnectionContext.java | 14 +-
.../odbc/odbc/OdbcConnectionContext.java | 12 +-
.../platform/client/ClientConnectionContext.java | 6 +-
.../processors/rest/GridRestProcessor.java | 1 +
.../rest/protocols/tcp/GridTcpRestNioListener.java | 1 +
.../processors/rest/request/GridRestRequest.java | 18 ++
.../ignite/internal/util/nio/GridNioSession.java | 6 +
.../internal/util/nio/GridNioSessionImpl.java | 20 ++
.../internal/util/nio/ssl/GridNioSslFilter.java | 2 +
.../plugin/security/AuthenticationContext.java | 21 ++
.../ignite/plugin/security/SecuritySubject.java | 10 +
modules/core/src/test/config/tests.properties | 1 +
.../client/ThinClientSslPermissionCheckTest.java | 237 +++++++++++++++++++++
.../TestCertificateSecurityPluginProvider.java | 40 ++++
....java => TestCertificateSecurityProcessor.java} | 91 +++-----
.../security/impl/TestSecurityProcessor.java | 1 +
.../security/impl/TestSecuritySubject.java | 19 ++
.../util/nio/impl/GridNioFilterChainSelfTest.java | 139 ------------
.../internal/util/nio/impl}/MockNioSession.java | 8 +-
.../tcp/TcpDiscoverySslTrustedUntrustedTest.java | 16 ++
.../ignite/testsuites/SecurityTestSuite.java | 2 +
.../util/GridCommandHandlerClusterByClassTest.java | 4 +-
...andHandlerClusterByClassTest_cache_help.output} | 0
...idCommandHandlerClusterByClassTest_help.output} | 0
...lerClusterByClassWithSSLTest_cache_help.output} | 0
...ndHandlerClusterByClassWithSSLTest_help.output} | 4 +-
.../protocols/http/jetty/GridJettyRestHandler.java | 6 +
parent/pom.xml | 3 +-
35 files changed, 586 insertions(+), 237 deletions(-)
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorCommonSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorCommonSelfTest.java
index 1b93284..5f14e2c 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorCommonSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorCommonSelfTest.java
@@ -108,12 +108,7 @@ public abstract class JettyRestProcessorCommonSelfTest extends AbstractRestProce
URL url = new URL(sb.toString());
- URLConnection conn = url.openConnection();
-
- String signature = signature();
-
- if (signature != null)
- conn.setRequestProperty("X-Signature", signature);
+ URLConnection conn = openConnection(url);
InputStream in = conn.getInputStream();
@@ -128,6 +123,24 @@ public abstract class JettyRestProcessorCommonSelfTest extends AbstractRestProce
}
/**
+ * Open REST connection, set signature header if needed.
+ *
+ * @param url URL to open.
+ * @return URL connection.
+ * @throws Exception If failed.
+ */
+ protected URLConnection openConnection(URL url) throws Exception {
+ URLConnection conn = url.openConnection();
+
+ String signature = signature();
+
+ if (signature != null)
+ conn.setRequestProperty("X-Signature", signature);
+
+ return conn;
+ }
+
+ /**
* @param cacheName Optional cache name.
* @param cmd REST command.
* @param params Command parameters.
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/TcpRestParserSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/TcpRestParserSelfTest.java
index 182149e..6780fa1 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/TcpRestParserSelfTest.java
+++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/TcpRestParserSelfTest.java
@@ -29,6 +29,7 @@ import org.apache.ignite.internal.processors.rest.client.message.GridClientCache
import org.apache.ignite.internal.processors.rest.client.message.GridClientHandshakeRequest;
import org.apache.ignite.internal.processors.rest.client.message.GridClientMessage;
import org.apache.ignite.internal.util.nio.GridNioSession;
+import org.apache.ignite.internal.util.nio.impl.MockNioSession;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.testframework.GridTestUtils;
diff --git a/modules/clients/src/test/keystore/node0102.jks b/modules/clients/src/test/keystore/node0102.jks
new file mode 100644
index 0000000..3e7cfd7
Binary files /dev/null and b/modules/clients/src/test/keystore/node0102.jks differ
diff --git a/modules/clients/src/test/resources/jetty/rest-jetty-ssl-client-auth.xml b/modules/clients/src/test/resources/jetty/rest-jetty-ssl-client-auth.xml
new file mode 100644
index 0000000..a1bd0dd
--- /dev/null
+++ b/modules/clients/src/test/resources/jetty/rest-jetty-ssl-client-auth.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0"?>
+
+<!--
+ 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.
+ -->
+
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+ <Arg name="threadPool">
+ <New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
+ <Set name="minThreads">5</Set>
+ <Set name="maxThreads">10</Set>
+ </New>
+ </Arg>
+
+ <New id="httpsCfg" class="org.eclipse.jetty.server.HttpConfiguration">
+ <Set name="secureScheme">https</Set>
+ <Set name="securePort"><SystemProperty name="IGNITE_JETTY_PORT" default="8091"/></Set>
+ <Set name="sendServerVersion">true</Set>
+ <Set name="sendDateHeader">true</Set>
+ <Call name="addCustomizer">
+ <Arg><New class="org.eclipse.jetty.server.SecureRequestCustomizer"/></Arg>
+ </Call>
+ </New>
+
+ <New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory$Server">
+ <Set name="keyStorePath"><SystemProperty
+ name="IGNITE_HOME" default="${IGNITE_HOME}"/>/modules/clients/src/test/keystore/server.jks</Set>
+ <Set name="keyStorePassword">123456</Set>
+ <Set name="keyManagerPassword">123456</Set>
+ <Set name="trustStorePath"><SystemProperty
+ name="IGNITE_HOME" default="${IGNITE_HOME}"/>/modules/clients/src/test/keystore/trust-both.jks</Set>
+ <Set name="trustStorePassword">123456</Set>
+ <Set name="needClientAuth">true</Set>
+ </New>
+
+ <Call name="addConnector">
+ <Arg>
+ <New class="org.eclipse.jetty.server.ServerConnector">
+ <Arg name="server">
+ <Ref refid="Server"/>
+ </Arg>
+ <Arg name="factories">
+ <Array type="org.eclipse.jetty.server.ConnectionFactory">
+ <Item>
+ <New class="org.eclipse.jetty.server.SslConnectionFactory">
+ <Arg><Ref refid="sslContextFactory"/></Arg>
+ <Arg>http/1.1</Arg>
+ </New>
+ </Item>
+ <Item>
+ <New class="org.eclipse.jetty.server.HttpConnectionFactory">
+ <Arg><Ref refid="httpsCfg"/></Arg>
+ </New>
+ </Item>
+ </Array>
+ </Arg>
+ <Set name="host"><SystemProperty name="IGNITE_JETTY_HOST" default="localhost"/></Set>
+ <Set name="port"><SystemProperty name="IGNITE_JETTY_PORT" default="8091"/></Set>
+ <Set name="idleTimeout">30000</Set>
+ <Set name="reuseAddress">true</Set>
+ </New>
+ </Arg>
+ </Call>
+
+ <Set name="handler">
+ <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
+ <Set name="handlers">
+ <Array type="org.eclipse.jetty.server.Handler">
+ <Item>
+ <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
+ </Item>
+ </Array>
+ </Set>
+ </New>
+ </Set>
+
+ <Set name="stopAtShutdown">false</Set>
+</Configure>
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerAbstractConnectionContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerAbstractConnectionContext.java
index 7220555..380c76b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerAbstractConnectionContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerAbstractConnectionContext.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.processors.odbc;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
+import java.security.cert.Certificate;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.processors.authentication.AuthorizationContext;
@@ -90,10 +91,10 @@ public abstract class ClientListenerAbstractConnectionContext implements ClientL
* @return Auth context.
* @throws IgniteCheckedException If failed.
*/
- protected AuthorizationContext authenticate(String user, String pwd)
+ protected AuthorizationContext authenticate(Certificate[] certificates, String user, String pwd)
throws IgniteCheckedException {
if (ctx.security().enabled())
- authCtx = authenticateExternal(user, pwd).authorizationContext();
+ authCtx = authenticateExternal(certificates, user, pwd).authorizationContext();
else if (ctx.authentication().enabled()) {
if (F.isEmpty(user))
throw new IgniteAccessControlException("Unauthenticated sessions are prohibited.");
@@ -112,7 +113,7 @@ public abstract class ClientListenerAbstractConnectionContext implements ClientL
/**
* Do 3-rd party authentication.
*/
- private AuthenticationContext authenticateExternal(String user, String pwd)
+ private AuthenticationContext authenticateExternal(Certificate[] certificates, String user, String pwd)
throws IgniteCheckedException {
SecurityCredentials cred = new SecurityCredentials(user, pwd);
@@ -122,6 +123,7 @@ public abstract class ClientListenerAbstractConnectionContext implements ClientL
authCtx.subjectId(UUID.randomUUID());
authCtx.nodeAttributes(F.isEmpty(userAttrs) ? Collections.emptyMap() : userAttrs);
authCtx.credentials(cred);
+ authCtx.certificates(certificates);
secCtx = ctx.security().authenticate(authCtx);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerConnectionContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerConnectionContext.java
index 2a27986..d0cd6ea 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerConnectionContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerConnectionContext.java
@@ -21,6 +21,7 @@ import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.internal.binary.BinaryReaderExImpl;
import org.apache.ignite.internal.processors.authentication.AuthorizationContext;
import org.apache.ignite.internal.processors.security.SecurityContext;
+import org.apache.ignite.internal.util.nio.GridNioSession;
import org.jetbrains.annotations.Nullable;
/**
@@ -46,11 +47,13 @@ public interface ClientListenerConnectionContext {
/**
* Initialize from handshake message.
*
+ *
+ * @param ses NIO session.
* @param ver Protocol version.
* @param reader Reader set to the configuration part of the handshake message.
* @throws IgniteCheckedException On error.
*/
- void initializeFromHandshake(ClientListenerProtocolVersion ver, BinaryReaderExImpl reader)
+ void initializeFromHandshake(GridNioSession ses, ClientListenerProtocolVersion ver, BinaryReaderExImpl reader)
throws IgniteCheckedException;
/**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
index fb4dfda..1bb4ffa 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
@@ -306,12 +306,12 @@ public class ClientListenerNioListener extends GridNioServerListenerAdapter<byte
ClientListenerConnectionContext connCtx = null;
try {
- connCtx = prepareContext(ses, clientType);
+ connCtx = prepareContext(clientType);
ensureClientPermissions(clientType);
if (connCtx.isVersionSupported(ver)) {
- connCtx.initializeFromHandshake(ver, reader);
+ connCtx.initializeFromHandshake(ses, ver, reader);
ses.addMeta(CONN_CTX_META_KEY, connCtx);
}
@@ -367,15 +367,15 @@ public class ClientListenerNioListener extends GridNioServerListenerAdapter<byte
* @return Context.
* @throws IgniteCheckedException If failed.
*/
- private ClientListenerConnectionContext prepareContext(GridNioSession ses, byte clientType) throws IgniteCheckedException {
+ private ClientListenerConnectionContext prepareContext(byte clientType) throws IgniteCheckedException {
long connId = nextConnectionId();
switch (clientType) {
case ODBC_CLIENT:
- return new OdbcConnectionContext(ctx, ses, busyLock, connId, maxCursors);
+ return new OdbcConnectionContext(ctx, busyLock, connId, maxCursors);
case JDBC_CLIENT:
- return new JdbcConnectionContext(ctx, ses, busyLock, connId, maxCursors);
+ return new JdbcConnectionContext(ctx, busyLock, connId, maxCursors);
case THIN_CLIENT:
return new ClientConnectionContext(ctx, connId, maxCursors, thinCfg);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcConnectionContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcConnectionContext.java
index 329af01..214ae11 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcConnectionContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcConnectionContext.java
@@ -70,9 +70,6 @@ public class JdbcConnectionContext extends ClientListenerAbstractConnectionConte
/** Supported versions. */
private static final Set<ClientListenerProtocolVersion> SUPPORTED_VERS = new HashSet<>();
- /** Session. */
- private final GridNioSession ses;
-
/** Shutdown busy lock. */
private final GridSpinBusyLock busyLock;
@@ -104,17 +101,15 @@ public class JdbcConnectionContext extends ClientListenerAbstractConnectionConte
/**
* Constructor.
- * @param ctx Kernal Context.
- * @param ses Session.
+ * @param ctx Kernal Context.
* @param busyLock Shutdown busy lock.
* @param connId Connection ID.
* @param maxCursors Maximum allowed cursors.
*/
- public JdbcConnectionContext(GridKernalContext ctx, GridNioSession ses, GridSpinBusyLock busyLock, long connId,
+ public JdbcConnectionContext(GridKernalContext ctx, GridSpinBusyLock busyLock, long connId,
int maxCursors) {
super(ctx, connId);
- this.ses = ses;
this.busyLock = busyLock;
this.maxCursors = maxCursors;
@@ -132,7 +127,8 @@ public class JdbcConnectionContext extends ClientListenerAbstractConnectionConte
}
/** {@inheritDoc} */
- @Override public void initializeFromHandshake(ClientListenerProtocolVersion ver, BinaryReaderExImpl reader)
+ @Override public void initializeFromHandshake(GridNioSession ses,
+ ClientListenerProtocolVersion ver, BinaryReaderExImpl reader)
throws IgniteCheckedException {
assert SUPPORTED_VERS.contains(ver): "Unsupported JDBC protocol version.";
@@ -192,7 +188,7 @@ public class JdbcConnectionContext extends ClientListenerAbstractConnectionConte
throw new IgniteCheckedException("Handshake error: " + e.getMessage(), e);
}
- actx = authenticate(user, passwd);
+ actx = authenticate(ses.certificates(), user, passwd);
}
parser = new JdbcMessageParser(ctx, ver);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcConnectionContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcConnectionContext.java
index 72a4a80..ccab26e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcConnectionContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcConnectionContext.java
@@ -62,9 +62,6 @@ public class OdbcConnectionContext extends ClientListenerAbstractConnectionConte
/** Supported versions. */
private static final Set<ClientListenerProtocolVersion> SUPPORTED_VERS = new HashSet<>();
- /** Session. */
- private final GridNioSession ses;
-
/** Shutdown busy lock. */
private final GridSpinBusyLock busyLock;
@@ -92,15 +89,13 @@ public class OdbcConnectionContext extends ClientListenerAbstractConnectionConte
/**
* Constructor.
* @param ctx Kernal Context.
- * @param ses Session.
* @param busyLock Shutdown busy lock.
* @param connId Connection ID.
* @param maxCursors Maximum allowed cursors.
*/
- public OdbcConnectionContext(GridKernalContext ctx, GridNioSession ses, GridSpinBusyLock busyLock, long connId, int maxCursors) {
+ public OdbcConnectionContext(GridKernalContext ctx, GridSpinBusyLock busyLock, long connId, int maxCursors) {
super(ctx, connId);
- this.ses = ses;
this.busyLock = busyLock;
this.maxCursors = maxCursors;
@@ -118,7 +113,8 @@ public class OdbcConnectionContext extends ClientListenerAbstractConnectionConte
}
/** {@inheritDoc} */
- @Override public void initializeFromHandshake(ClientListenerProtocolVersion ver, BinaryReaderExImpl reader)
+ @Override public void initializeFromHandshake(GridNioSession ses,
+ ClientListenerProtocolVersion ver, BinaryReaderExImpl reader)
throws IgniteCheckedException {
assert SUPPORTED_VERS.contains(ver): "Unsupported ODBC protocol version.";
@@ -153,7 +149,7 @@ public class OdbcConnectionContext extends ClientListenerAbstractConnectionConte
nestedTxMode = NestedTxMode.fromByte(nestedTxModeVal);
}
- AuthorizationContext actx = authenticate(user, passwd);
+ AuthorizationContext actx = authenticate(ses.certificates(), user, passwd);
ClientListenerResponseSender sender = new ClientListenerResponseSender() {
@Override public void send(ClientListenerResponse resp) {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientConnectionContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientConnectionContext.java
index 7fc9eb7..6dd0cdd 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientConnectionContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientConnectionContext.java
@@ -36,6 +36,7 @@ import org.apache.ignite.internal.processors.odbc.ClientListenerMessageParser;
import org.apache.ignite.internal.processors.odbc.ClientListenerProtocolVersion;
import org.apache.ignite.internal.processors.odbc.ClientListenerRequestHandler;
import org.apache.ignite.internal.processors.platform.client.tx.ClientTxContext;
+import org.apache.ignite.internal.util.nio.GridNioSession;
/**
* Thin Client connection context.
@@ -155,7 +156,8 @@ public class ClientConnectionContext extends ClientListenerAbstractConnectionCon
}
/** {@inheritDoc} */
- @Override public void initializeFromHandshake(ClientListenerProtocolVersion ver, BinaryReaderExImpl reader)
+ @Override public void initializeFromHandshake(GridNioSession ses,
+ ClientListenerProtocolVersion ver, BinaryReaderExImpl reader)
throws IgniteCheckedException {
boolean hasMore;
@@ -179,7 +181,7 @@ public class ClientConnectionContext extends ClientListenerAbstractConnectionCon
}
}
- AuthorizationContext authCtx = authenticate(user, pwd);
+ AuthorizationContext authCtx = authenticate(ses.certificates(), user, pwd);
currentVer = ver;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java
index 8b97405..9f8bdb5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java
@@ -805,6 +805,7 @@ public class GridRestProcessor extends GridProcessorAdapter implements IgniteRes
authCtx.subjectId(req.clientId());
authCtx.nodeAttributes(req.userAttributes());
authCtx.address(req.address());
+ authCtx.certificates(req.certificates());
SecurityCredentials creds = credentials(req);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestNioListener.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestNioListener.java
index 6ad9431..10f895c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestNioListener.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestNioListener.java
@@ -416,6 +416,7 @@ public class GridTcpRestNioListener extends GridNioServerListenerAdapter<GridCli
restReq.clientId(msg.clientId());
restReq.sessionToken(msg.sessionToken());
restReq.address(ses.remoteAddress());
+ restReq.certificates(ses.certificates());
if (restReq.credentials() == null) {
GridClientAbstractMessage msg0 = (GridClientAbstractMessage) msg;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/GridRestRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/GridRestRequest.java
index 42a687b..e5c9dc8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/GridRestRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/request/GridRestRequest.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.processors.rest.request;
import java.net.InetSocketAddress;
+import java.security.cert.Certificate;
import java.util.Map;
import java.util.UUID;
import org.apache.ignite.internal.processors.authentication.AuthorizationContext;
@@ -55,6 +56,9 @@ public class GridRestRequest {
/** User attributes. */
Map<String, String> userAttrs;
+ /** */
+ private Certificate[] certs;
+
/**
* @return Destination ID.
*/
@@ -183,6 +187,20 @@ public class GridRestRequest {
this.userAttrs = userAttrs;
}
+ /**
+ * @return Client SSL certificates.
+ */
+ public Certificate[] certificates() {
+ return certs;
+ }
+
+ /**
+ * @param certs Client SSL certificates.
+ */
+ public void certificates(Certificate[] certs) {
+ this.certs = certs;
+ }
+
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(GridRestRequest.class, this);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioSession.java b/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioSession.java
index 12b9b40b..979bc9f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioSession.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioSession.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.util.nio;
import java.net.InetSocketAddress;
+import java.security.cert.Certificate;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.lang.IgniteInClosure;
@@ -146,6 +147,11 @@ public interface GridNioSession {
public boolean accepted();
/**
+ * @return Client SSL certificates
+ */
+ @Nullable public Certificate[] certificates();
+
+ /**
* Resumes session reads.
*
* @return Future representing result.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionImpl.java
index 51cb558..d1574b6 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionImpl.java
@@ -18,15 +18,19 @@
package org.apache.ignite.internal.util.nio;
import java.net.InetSocketAddress;
+import java.security.cert.Certificate;
import java.util.concurrent.atomic.AtomicLong;
+import javax.net.ssl.SSLPeerUnverifiedException;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.util.nio.ssl.GridSslMeta;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteInClosure;
import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.MAX_KEYS_CNT;
+import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.SSL_META;
/**
*
@@ -270,6 +274,22 @@ public class GridNioSessionImpl implements GridNioSession {
return accepted;
}
+ /** */
+ @Override public Certificate[] certificates() {
+ GridSslMeta meta = meta(SSL_META.ordinal());
+
+ if (meta != null) {
+ try {
+ return meta.sslEngine().getSession().getPeerCertificates();
+ }
+ catch (SSLPeerUnverifiedException e) {
+ // Nothing to do.
+ }
+ }
+
+ return null;
+ }
+
/**
* @param <T> Chain type.
* @return Filter chain.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/nio/ssl/GridNioSslFilter.java b/modules/core/src/main/java/org/apache/ignite/internal/util/nio/ssl/GridNioSslFilter.java
index 9b0c91c..7167c40 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/nio/ssl/GridNioSslFilter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/nio/ssl/GridNioSslFilter.java
@@ -175,6 +175,8 @@ public class GridNioSslFilter extends GridNioFilterAdapter {
sslMeta = new GridSslMeta();
+ sslMeta.sslEngine(engine);
+
ses.addMeta(SSL_META.ordinal(), sslMeta);
handshake = true;
diff --git a/modules/core/src/main/java/org/apache/ignite/plugin/security/AuthenticationContext.java b/modules/core/src/main/java/org/apache/ignite/plugin/security/AuthenticationContext.java
index f48b697..3010c19 100644
--- a/modules/core/src/main/java/org/apache/ignite/plugin/security/AuthenticationContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/plugin/security/AuthenticationContext.java
@@ -18,6 +18,7 @@
package org.apache.ignite.plugin.security;
import java.net.InetSocketAddress;
+import java.security.cert.Certificate;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -50,6 +51,9 @@ public class AuthenticationContext {
/** True if this is a client node context. */
private boolean client;
+ /** Client SSL certificates. */
+ private Certificate[] certs;
+
/**
* Gets subject type.
*
@@ -158,6 +162,23 @@ public class AuthenticationContext {
}
/**
+ * @return Client SSL certificates.
+ */
+ public Certificate[] certificates() {
+ return certs;
+ }
+
+ /**
+ * Set client SSL certificates.
+ * @param certs Client SSL certificates.
+ */
+ public AuthenticationContext certificates(Certificate[] certs) {
+ this.certs = certs;
+
+ return this;
+ }
+
+ /**
* @return {@code true} if this is a client node context.
*/
public boolean isClient() {
diff --git a/modules/core/src/main/java/org/apache/ignite/plugin/security/SecuritySubject.java b/modules/core/src/main/java/org/apache/ignite/plugin/security/SecuritySubject.java
index 6b3e6f6..5d4e624 100644
--- a/modules/core/src/main/java/org/apache/ignite/plugin/security/SecuritySubject.java
+++ b/modules/core/src/main/java/org/apache/ignite/plugin/security/SecuritySubject.java
@@ -21,6 +21,7 @@ import java.io.Serializable;
import java.net.InetSocketAddress;
import java.security.PermissionCollection;
import java.security.ProtectionDomain;
+import java.security.cert.Certificate;
import java.util.UUID;
import org.apache.ignite.internal.processors.security.SecurityUtils;
@@ -57,6 +58,15 @@ public interface SecuritySubject extends Serializable {
public InetSocketAddress address();
/**
+ * Gets subject client certificates, or {@code null} if SSL were not used or client certificate checking not enabled.
+ *
+ * @return Subject client certificates.
+ */
+ public default Certificate[] certificates() {
+ return null;
+ }
+
+ /**
* Authorized permission set for the subject.
*
* @return Authorized permission set for the subject.
diff --git a/modules/core/src/test/config/tests.properties b/modules/core/src/test/config/tests.properties
index d659224..d379203 100644
--- a/modules/core/src/test/config/tests.properties
+++ b/modules/core/src/test/config/tests.properties
@@ -148,6 +148,7 @@ ssl.keystore.node01.path=@{IGNITE_HOME}/modules/clients/src/test/keystore/node01
ssl.keystore.node02.path=@{IGNITE_HOME}/modules/clients/src/test/keystore/node02.jks
ssl.keystore.node02old.path=@{IGNITE_HOME}/modules/clients/src/test/keystore/node02old.jks
ssl.keystore.node03.path=@{IGNITE_HOME}/modules/clients/src/test/keystore/node03.jks
+ssl.keystore.node0102.path=@{IGNITE_HOME}/modules/clients/src/test/keystore/node0102.jks
# Cluster certificate is signed by trust-one, thinServer and thinClient – by trust-two,
# connectorServer and connectorClient – by trust-three.
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/client/ThinClientSslPermissionCheckTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/client/ThinClientSslPermissionCheckTest.java
new file mode 100644
index 0000000..d65f2e4
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/client/ThinClientSslPermissionCheckTest.java
@@ -0,0 +1,237 @@
+/*
+ * 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.processors.security.client;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.client.ClientAuthorizationException;
+import org.apache.ignite.client.Config;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.client.SslMode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.configuration.ClientConnectorConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.security.AbstractSecurityTest;
+import org.apache.ignite.internal.processors.security.impl.TestCertificateSecurityPluginProvider;
+import org.apache.ignite.internal.processors.security.impl.TestSecurityData;
+import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.plugin.security.SecurityPermissionSetBuilder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static java.util.Collections.singletonMap;
+import static org.apache.ignite.internal.util.lang.GridFunc.t;
+import static org.apache.ignite.plugin.security.SecurityPermission.CACHE_CREATE;
+import static org.apache.ignite.plugin.security.SecurityPermission.CACHE_DESTROY;
+import static org.apache.ignite.plugin.security.SecurityPermission.CACHE_PUT;
+import static org.apache.ignite.plugin.security.SecurityPermission.CACHE_READ;
+import static org.apache.ignite.plugin.security.SecurityPermission.CACHE_REMOVE;
+import static org.apache.ignite.plugin.security.SecurityPermission.TASK_EXECUTE;
+import static org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
+
+/**
+ * Security tests for thin client.
+ */
+@RunWith(JUnit4.class)
+public class ThinClientSslPermissionCheckTest extends AbstractSecurityTest {
+ /** Client. */
+ private static final String CLIENT = "node01";
+
+ /** Client that has system permissions. */
+ private static final String CLIENT_SYS_PERM = "node02";
+
+ /** Client that has system permissions. */
+ private static final String CLIENT_CACHE_TASK_OPER = "node03";
+
+ /** Cache. */
+ private static final String CACHE = "TEST_CACHE";
+
+ /** Forbidden cache. */
+ private static final String FORBIDDEN_CACHE = "FORBIDDEN_TEST_CACHE";
+
+ /** Cache to test system oper permissions. */
+ private static final String DYNAMIC_CACHE = "DYNAMIC_TEST_CACHE";
+
+ /** Remove all task name. */
+ public static final String REMOVE_ALL_TASK =
+ "org.apache.ignite.internal.processors.cache.distributed.GridDistributedCacheAdapter$RemoveAllTask";
+
+ /** Clear task name. */
+ public static final String CLEAR_TASK =
+ "org.apache.ignite.internal.processors.cache.GridCacheAdapter$ClearTask";
+
+ /**
+ * @param clientData Array of client security data.
+ */
+ private IgniteConfiguration getConfiguration(TestSecurityData... clientData) throws Exception {
+ return getConfiguration(G.allGrids().size(), clientData);
+ }
+
+ /**
+ * @param idx Index.
+ * @param clientData Array of client security data.
+ */
+ private IgniteConfiguration getConfiguration(int idx, TestSecurityData... clientData) throws Exception {
+ String instanceName = getTestIgniteInstanceName(idx);
+
+ return getConfiguration(
+ instanceName,
+ new TestCertificateSecurityPluginProvider(clientData)
+ ).setCacheConfiguration(
+ new CacheConfiguration().setName(CACHE),
+ new CacheConfiguration().setName(FORBIDDEN_CACHE)
+ ).setClientConnectorConfiguration(
+ new ClientConnectorConfiguration().setSslEnabled(true).setSslClientAuth(true)
+ .setUseIgniteSslContextFactory(false)
+ .setSslContextFactory(GridTestUtils.sslTrustedFactory("node01", "trustboth"))
+ );
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTestsStarted() throws Exception {
+ IgniteEx ignite = startGrid(
+ getConfiguration(
+ new TestSecurityData(CLIENT,
+ SecurityPermissionSetBuilder.create().defaultAllowAll(false)
+ .appendCachePermissions(CACHE, CACHE_READ, CACHE_PUT, CACHE_REMOVE)
+ .appendCachePermissions(FORBIDDEN_CACHE, EMPTY_PERMS)
+ .build()
+ ),
+ new TestSecurityData(CLIENT_SYS_PERM,
+ SecurityPermissionSetBuilder.create().defaultAllowAll(false)
+ .appendSystemPermissions(CACHE_CREATE, CACHE_DESTROY)
+ .build()
+ ),
+ new TestSecurityData(CLIENT_CACHE_TASK_OPER,
+ SecurityPermissionSetBuilder.create().defaultAllowAll(false)
+ .appendCachePermissions(CACHE, CACHE_REMOVE)
+ .appendTaskPermissions(REMOVE_ALL_TASK, TASK_EXECUTE)
+ .appendTaskPermissions(CLEAR_TASK, TASK_EXECUTE)
+ .build()
+ )
+ )
+ );
+
+ ignite.cluster().active(true);
+ }
+
+ /** */
+ @Test
+ public void testCacheSinglePermOperations() throws Exception {
+ for (IgniteBiTuple<Consumer<IgniteClient>, String> t : operations(CACHE))
+ runOperation(CLIENT, t);
+
+ for (IgniteBiTuple<Consumer<IgniteClient>, String> t : operations(FORBIDDEN_CACHE))
+ assertThrowsWithCause(() -> runOperation(CLIENT, t), ClientAuthorizationException.class);
+ }
+
+ /**
+ * That test shows the wrong case when a client has permission for a remove operation
+ * but a removeAll operation is forbidden for it. To have permission for the removeAll (clear) operation
+ * a client need to have the permission to execute {@link #REMOVE_ALL_TASK} ({@link #CLEAR_TASK}) task.
+ *
+ * @throws Exception If error occurs.
+ */
+ @Test
+ public void testCacheTaskPermOperations() throws Exception {
+ List<IgniteBiTuple<Consumer<IgniteClient>, String>> ops = Arrays.asList(
+ t(c -> c.cache(CACHE).removeAll(), "removeAll"),
+ t(c -> c.cache(CACHE).clear(), "clear")
+ );
+
+ for (IgniteBiTuple<Consumer<IgniteClient>, String> op : ops) {
+ runOperation(CLIENT_CACHE_TASK_OPER, op);
+
+ assertThrowsWithCause(() -> runOperation(CLIENT, op), ClientAuthorizationException.class);
+ }
+ }
+
+ /** */
+ @Test
+ public void testSysOperation() throws Exception {
+ try (IgniteClient sysPrmClnt = startClient(CLIENT_SYS_PERM)) {
+ sysPrmClnt.createCache(DYNAMIC_CACHE);
+
+ assertTrue(sysPrmClnt.cacheNames().contains(DYNAMIC_CACHE));
+
+ sysPrmClnt.destroyCache(DYNAMIC_CACHE);
+
+ assertFalse(sysPrmClnt.cacheNames().contains(DYNAMIC_CACHE));
+ }
+
+ List<IgniteBiTuple<Consumer<IgniteClient>, String>> ops = Arrays.asList(
+ t(c -> c.createCache(DYNAMIC_CACHE), "createCache"),
+ t(c -> c.destroyCache(CACHE), "destroyCache")
+ );
+
+ for (IgniteBiTuple<Consumer<IgniteClient>, String> op : ops)
+ assertThrowsWithCause(() -> runOperation(CLIENT, op), ClientAuthorizationException.class);
+ }
+
+ /**
+ * @param cacheName Cache name.
+ */
+ private Collection<IgniteBiTuple<Consumer<IgniteClient>, String>> operations(final String cacheName) {
+ return Arrays.asList(
+ t(c -> c.cache(cacheName).put("key", "value"), "put"),
+ t(c -> c.cache(cacheName).putAll(singletonMap("key", "value")), "putAll"),
+ t(c -> c.cache(cacheName).get("key"), "get)"),
+ t(c -> c.cache(cacheName).getAll(Collections.singleton("key")), "getAll"),
+ t(c -> c.cache(cacheName).containsKey("key"), "containsKey"),
+ t(c -> c.cache(cacheName).remove("key"), "remove"),
+ t(c -> c.cache(cacheName).replace("key", "value"), "replace"),
+ t(c -> c.cache(cacheName).putIfAbsent("key", "value"), "putIfAbsent"),
+ t(c -> c.cache(cacheName).getAndPut("key", "value"), "getAndPut"),
+ t(c -> c.cache(cacheName).getAndRemove("key"), "getAndRemove"),
+ t(c -> c.cache(cacheName).getAndReplace("key", "value"), "getAndReplace")
+ );
+ }
+
+ /** */
+ private void runOperation(String clientName, IgniteBiTuple<Consumer<IgniteClient>, String> op) {
+ try (IgniteClient client = startClient(clientName)) {
+ op.get1().accept(client);
+ }
+ catch (Exception e) {
+ throw new IgniteException(op.get2(), e);
+ }
+ }
+
+ /**
+ * @param userName User name.
+ */
+ private IgniteClient startClient(String userName) {
+ return Ignition.startClient(
+ new ClientConfiguration().setAddresses(Config.SERVER).setSslMode(SslMode.REQUIRED)
+ .setSslClientCertificateKeyStorePath(GridTestUtils.keyStorePath(userName))
+ .setSslClientCertificateKeyStorePassword(GridTestUtils.keyStorePassword())
+ .setSslTrustCertificateKeyStorePath(GridTestUtils.keyStorePath("trustone"))
+ .setSslTrustCertificateKeyStorePassword(GridTestUtils.keyStorePassword())
+ );
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestCertificateSecurityPluginProvider.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestCertificateSecurityPluginProvider.java
new file mode 100644
index 0000000..818f306
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestCertificateSecurityPluginProvider.java
@@ -0,0 +1,40 @@
+/*
+ * 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.processors.security.impl;
+
+import java.util.List;
+import java.util.Arrays;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.processors.security.AbstractTestSecurityPluginProvider;
+import org.apache.ignite.internal.processors.security.GridSecurityProcessor;
+
+/** */
+public class TestCertificateSecurityPluginProvider extends AbstractTestSecurityPluginProvider {
+ /** Users security data. */
+ private final List<TestSecurityData> clientData;
+
+ /** */
+ public TestCertificateSecurityPluginProvider(TestSecurityData... clientData) {
+ this.clientData = Arrays.asList(clientData);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected GridSecurityProcessor securityProcessor(GridKernalContext ctx) {
+ return new TestCertificateSecurityProcessor(ctx, clientData);
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityProcessor.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestCertificateSecurityProcessor.java
similarity index 64%
copy from modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityProcessor.java
copy to modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestCertificateSecurityProcessor.java
index dc212cd..a59f644 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityProcessor.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestCertificateSecurityProcessor.java
@@ -18,7 +18,8 @@
package org.apache.ignite.internal.processors.security.impl;
import java.net.InetSocketAddress;
-import java.security.Permissions;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -34,90 +35,80 @@ import org.apache.ignite.internal.processors.security.GridSecurityProcessor;
import org.apache.ignite.internal.processors.security.SecurityContext;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.plugin.security.AuthenticationContext;
-import org.apache.ignite.plugin.security.SecurityBasicPermissionSet;
import org.apache.ignite.plugin.security.SecurityCredentials;
import org.apache.ignite.plugin.security.SecurityException;
import org.apache.ignite.plugin.security.SecurityPermission;
import org.apache.ignite.plugin.security.SecurityPermissionSet;
import org.apache.ignite.plugin.security.SecuritySubject;
+import static org.apache.ignite.plugin.security.SecurityPermissionSetBuilder.ALLOW_ALL;
import static org.apache.ignite.plugin.security.SecuritySubjectType.REMOTE_NODE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
/**
* Security processor for test.
*/
-public class TestSecurityProcessor extends GridProcessorAdapter implements GridSecurityProcessor {
+public class TestCertificateSecurityProcessor extends GridProcessorAdapter implements GridSecurityProcessor {
/** Permissions. */
- public static final Map<SecurityCredentials, SecurityPermissionSet> PERMS = new ConcurrentHashMap<>();
-
- /** Sandbox permissions. */
- private static final Map<SecurityCredentials, Permissions> SANDBOX_PERMS = new ConcurrentHashMap<>();
-
- /** Node security data. */
- private final TestSecurityData nodeSecData;
+ public static final Map<String, SecurityPermissionSet> PERMS = new ConcurrentHashMap<>();
/** Users security data. */
private final Collection<TestSecurityData> predefinedAuthData;
- /** Global authentication. */
- private final boolean globalAuth;
-
/**
* Constructor.
*/
- public TestSecurityProcessor(GridKernalContext ctx, TestSecurityData nodeSecData,
- Collection<TestSecurityData> predefinedAuthData, boolean globalAuth) {
+ public TestCertificateSecurityProcessor(GridKernalContext ctx, Collection<TestSecurityData> predefinedAuthData) {
super(ctx);
- this.nodeSecData = nodeSecData;
this.predefinedAuthData = predefinedAuthData.isEmpty()
? Collections.emptyList()
: new ArrayList<>(predefinedAuthData);
- this.globalAuth = globalAuth;
}
/** {@inheritDoc} */
- @Override public SecurityContext authenticateNode(ClusterNode node, SecurityCredentials cred)
- throws IgniteCheckedException {
- if (!PERMS.containsKey(cred))
- return null;
-
+ @Override public SecurityContext authenticateNode(ClusterNode node, SecurityCredentials cred) {
return new TestSecurityContext(
new TestSecuritySubject()
.setType(REMOTE_NODE)
.setId(node.id())
.setAddr(new InetSocketAddress(F.first(node.addresses()), 0))
- .setLogin(cred.getLogin())
- .setPerms(PERMS.get(cred))
- .sandboxPermissions(SANDBOX_PERMS.get(cred))
+ .setLogin("")
+ .setPerms(ALLOW_ALL)
);
}
/** {@inheritDoc} */
@Override public boolean isGlobalNodeAuthentication() {
- return globalAuth;
+ return true;
}
/** {@inheritDoc} */
- @Override public SecurityContext authenticate(AuthenticationContext ctx) throws IgniteCheckedException {
- if (ctx.credentials() == null || ctx.credentials().getLogin() == null)
- return null;
+ @Override public SecurityContext authenticate(AuthenticationContext ctx) {
+ Certificate[] certs = ctx.certificates();
+
+ assertNotNull(certs);
+
+ assertEquals(2, certs.length);
- SecurityPermissionSet perms = PERMS.get(ctx.credentials());
+ assertTrue(((X509Certificate)certs[0]).getSubjectDN().getName().matches("^CN=[a-z0-9]+$"));
+ assertTrue(((X509Certificate)certs[0]).getIssuerDN().getName().startsWith("C=RU, ST=SPb, L=SPb, O=Ignite, OU=Dev"));
- if (perms == null) {
- perms = new SecurityBasicPermissionSet();
- ((SecurityBasicPermissionSet) perms).setDefaultAllowAll(true);
- }
+ String cn = ((X509Certificate)certs[0]).getSubjectDN().getName().substring(3);
+
+ if (!PERMS.containsKey(cn))
+ return null;
return new TestSecurityContext(
new TestSecuritySubject()
.setType(ctx.subjectType())
.setId(ctx.subjectId())
.setAddr(ctx.address())
- .setLogin(ctx.credentials().getLogin())
- .setPerms(perms)
- .sandboxPermissions(SANDBOX_PERMS.get(ctx.credentials()))
+ .setLogin(cn)
+ .setPerms(PERMS.get(cn))
+ .setCerts(ctx.certificates())
);
}
@@ -134,6 +125,7 @@ public class TestSecurityProcessor extends GridProcessorAdapter implements GridS
/** {@inheritDoc} */
@Override public void authorize(String name, SecurityPermission perm, SecurityContext securityCtx)
throws SecurityException {
+
if (!((TestSecurityContext)securityCtx).operationAllowed(name, perm))
throw new SecurityException("Authorization failed [perm=" + perm +
", name=" + name +
@@ -154,32 +146,17 @@ public class TestSecurityProcessor extends GridProcessorAdapter implements GridS
@Override public void start() throws IgniteCheckedException {
super.start();
- PERMS.put(nodeSecData.credentials(), nodeSecData.getPermissions());
- SANDBOX_PERMS.put(nodeSecData.credentials(), nodeSecData.sandboxPermissions());
+ ctx.addNodeAttribute(IgniteNodeAttributes.ATTR_SECURITY_CREDENTIALS, new SecurityCredentials("", ""));
- ctx.addNodeAttribute(IgniteNodeAttributes.ATTR_SECURITY_CREDENTIALS, nodeSecData.credentials());
-
- for (TestSecurityData data : predefinedAuthData) {
- PERMS.put(data.credentials(), data.getPermissions());
- SANDBOX_PERMS.put(nodeSecData.credentials(), data.sandboxPermissions());
- }
+ for (TestSecurityData data : predefinedAuthData)
+ PERMS.put(data.credentials().getLogin().toString(), data.getPermissions());
}
/** {@inheritDoc} */
@Override public void stop(boolean cancel) throws IgniteCheckedException {
super.stop(cancel);
- PERMS.remove(nodeSecData.credentials());
- SANDBOX_PERMS.remove(nodeSecData.credentials());
-
- for (TestSecurityData data : predefinedAuthData) {
- PERMS.remove(data.credentials());
- SANDBOX_PERMS.remove(data.credentials());
- }
- }
-
- /** {@inheritDoc} */
- @Override public boolean sandboxEnabled() {
- return true;
+ for (TestSecurityData data : predefinedAuthData)
+ PERMS.remove(data.credentials().getLogin().toString());
}
}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityProcessor.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityProcessor.java
index dc212cd..d4e46c0 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityProcessor.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecurityProcessor.java
@@ -117,6 +117,7 @@ public class TestSecurityProcessor extends GridProcessorAdapter implements GridS
.setAddr(ctx.address())
.setLogin(ctx.credentials().getLogin())
.setPerms(perms)
+ .setCerts(ctx.certificates())
.sandboxPermissions(SANDBOX_PERMS.get(ctx.credentials()))
);
}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecuritySubject.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecuritySubject.java
index c41026e..2e27355 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecuritySubject.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/security/impl/TestSecuritySubject.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.processors.security.impl;
import java.net.InetSocketAddress;
import java.security.PermissionCollection;
+import java.security.cert.Certificate;
import java.util.UUID;
import org.apache.ignite.plugin.security.SecurityPermissionSet;
import org.apache.ignite.plugin.security.SecuritySubject;
@@ -46,6 +47,9 @@ public class TestSecuritySubject implements SecuritySubject {
/** Permissions for Sandbox checks. */
private PermissionCollection sandboxPerms;
+ /** Client certificates. */
+ private Certificate[] certs;
+
/**
* Default constructor.
*/
@@ -152,6 +156,21 @@ public class TestSecuritySubject implements SecuritySubject {
}
/** {@inheritDoc} */
+ @Override public Certificate[] certificates() {
+ return certs;
+ }
+
+ /**
+ * @param perms Permissions.
+ */
+ public TestSecuritySubject setCerts(Certificate[] certs) {
+ this.certs = certs;
+
+ return this;
+ }
+
+
+ /** {@inheritDoc} */
@Override public String toString() {
return "TestSecuritySubject{" +
"id=" + id +
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/nio/impl/GridNioFilterChainSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/nio/impl/GridNioFilterChainSelfTest.java
index 3dd3565..2ed6711 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/util/nio/impl/GridNioFilterChainSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/nio/impl/GridNioFilterChainSelfTest.java
@@ -17,17 +17,13 @@
package org.apache.ignite.internal.util.nio.impl;
-import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
-import org.apache.ignite.internal.util.lang.GridMetadataAwareAdapter;
import org.apache.ignite.internal.util.nio.GridNioFilterAdapter;
import org.apache.ignite.internal.util.nio.GridNioFilterChain;
-import org.apache.ignite.internal.util.nio.GridNioFinishedFuture;
import org.apache.ignite.internal.util.nio.GridNioFuture;
-import org.apache.ignite.internal.util.nio.GridNioRecoveryDescriptor;
import org.apache.ignite.internal.util.nio.GridNioServerListener;
import org.apache.ignite.internal.util.nio.GridNioServerListenerAdapter;
import org.apache.ignite.internal.util.nio.GridNioSession;
@@ -267,139 +263,4 @@ public class GridNioFilterChainSelfTest extends GridCommonAbstractTest {
ses.addMeta(metaKey, att);
}
}
-
- /**
- */
- public class MockNioSession extends GridMetadataAwareAdapter implements GridNioSession {
- /** Local address */
- private InetSocketAddress locAddr = new InetSocketAddress(0);
-
- /** Remote address. */
- private InetSocketAddress rmtAddr = new InetSocketAddress(0);
-
- /**
- * Creates empty mock session.
- */
- public MockNioSession() {
- // No-op.
- }
-
- /**
- * Creates new mock session with given addresses.
- *
- * @param locAddr Local address.
- * @param rmtAddr Remote address.
- */
- public MockNioSession(InetSocketAddress locAddr, InetSocketAddress rmtAddr) {
- this();
-
- this.locAddr = locAddr;
- this.rmtAddr = rmtAddr;
- }
-
- /** {@inheritDoc} */
- @Override public InetSocketAddress localAddress() {
- return locAddr;
- }
-
- /** {@inheritDoc} */
- @Override public InetSocketAddress remoteAddress() {
- return rmtAddr;
- }
-
- /** {@inheritDoc} */
- @Override public long bytesSent() {
- return 0;
- }
-
- /** {@inheritDoc} */
- @Override public long bytesReceived() {
- return 0;
- }
-
- /** {@inheritDoc} */
- @Override public long createTime() {
- return 0;
- }
-
- /** {@inheritDoc} */
- @Override public long closeTime() {
- return 0;
- }
-
- /** {@inheritDoc} */
- @Override public long lastReceiveTime() {
- return 0;
- }
-
- /** {@inheritDoc} */
- @Override public long lastSendTime() {
- return 0;
- }
-
- /** {@inheritDoc} */
- @Override public long lastSendScheduleTime() {
- return 0;
- }
-
- /** {@inheritDoc} */
- @Override public GridNioFuture<Boolean> close() {
- return new GridNioFinishedFuture<>(true);
- }
-
- /** {@inheritDoc} */
- @Override public GridNioFuture<?> send(Object msg) {
- return new GridNioFinishedFuture<>(true);
- }
-
- /** {@inheritDoc} */
- @Override public void sendNoFuture(Object msg, IgniteInClosure<IgniteException> ackC) {
- // No-op.
- }
-
- /** {@inheritDoc} */
- @Override public GridNioFuture<Object> resumeReads() {
- return null;
- }
-
- /** {@inheritDoc} */
- @Override public GridNioFuture<Object> pauseReads() {
- return null;
- }
-
- /** {@inheritDoc} */
- @Override public boolean accepted() {
- return false;
- }
-
- /** {@inheritDoc} */
- @Override public boolean readsPaused() {
- return false;
- }
-
- /** {@inheritDoc} */
- @Override public void outRecoveryDescriptor(GridNioRecoveryDescriptor recoveryDesc) {
- // No-op.
- }
-
- /** {@inheritDoc} */
- @Nullable @Override public GridNioRecoveryDescriptor outRecoveryDescriptor() {
- return null;
- }
-
- /** {@inheritDoc} */
- @Override public void inRecoveryDescriptor(GridNioRecoveryDescriptor recoveryDesc) {
- // No-op.
- }
-
- /** {@inheritDoc} */
- @Nullable @Override public GridNioRecoveryDescriptor inRecoveryDescriptor() {
- return null;
- }
-
- /** {@inheritDoc} */
- @Override public void systemMessage(Object msg) {
- // No-op.
- }
- }
}
diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/MockNioSession.java b/modules/core/src/test/java/org/apache/ignite/internal/util/nio/impl/MockNioSession.java
similarity index 95%
rename from modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/MockNioSession.java
rename to modules/core/src/test/java/org/apache/ignite/internal/util/nio/impl/MockNioSession.java
index a436ec4..0994ffc 100644
--- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/protocols/tcp/MockNioSession.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/nio/impl/MockNioSession.java
@@ -15,9 +15,10 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.processors.rest.protocols.tcp;
+package org.apache.ignite.internal.util.nio.impl;
import java.net.InetSocketAddress;
+import java.security.cert.Certificate;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.internal.util.lang.GridMetadataAwareAdapter;
@@ -135,6 +136,11 @@ public class MockNioSession extends GridMetadataAwareAdapter implements GridNioS
}
/** {@inheritDoc} */
+ @Override public Certificate[] certificates() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
@Override public boolean readsPaused() {
return false;
}
diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySslTrustedUntrustedTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySslTrustedUntrustedTest.java
index d0188da..e44db54 100644
--- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySslTrustedUntrustedTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySslTrustedUntrustedTest.java
@@ -77,6 +77,22 @@ public class TcpDiscoverySslTrustedUntrustedTest extends GridCommonAbstractTest
* @throws Exception If failed.
*/
@Test
+ public void testTrustOneMultiCert() throws Exception {
+ checkDiscoverySuccess("node01", "trustone", "node0102", "trustone");
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testTrustBothMultiCert() throws Exception {
+ checkDiscoverySuccess("node03", "trustboth", "node0102", "trusttwo");
+ }
+
+ /**
+ * @throws Exception If failed.
+ */
+ @Test
public void testDifferentCa() throws Exception {
checkDiscoveryFailure("node01", "trustone", "node02", "trusttwo");
}
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/SecurityTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/SecurityTestSuite.java
index b38f491..e466e14 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/SecurityTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/SecurityTestSuite.java
@@ -33,6 +33,7 @@ import org.apache.ignite.internal.processors.security.client.AdditionalSecurityC
import org.apache.ignite.internal.processors.security.client.AdditionalSecurityCheckWithGlobalAuthTest;
import org.apache.ignite.internal.processors.security.client.ThinClientPermissionCheckSecurityTest;
import org.apache.ignite.internal.processors.security.client.ThinClientPermissionCheckTest;
+import org.apache.ignite.internal.processors.security.client.ThinClientSslPermissionCheckTest;
import org.apache.ignite.internal.processors.security.compute.ComputePermissionCheckTest;
import org.apache.ignite.internal.processors.security.compute.closure.ComputeTaskCancelRemoteSecurityContextCheckTest;
import org.apache.ignite.internal.processors.security.compute.closure.ComputeTaskRemoteSecurityContextCheckTest;
@@ -75,6 +76,7 @@ import org.junit.runners.Suite;
CacheLoadRemoteSecurityContextCheckTest.class,
ContinuousQueryRemoteSecurityContextCheckTest.class,
ContinuousQueryWithTransformerRemoteSecurityContextCheckTest.class,
+ ThinClientSslPermissionCheckTest.class,
InvalidServerTest.class,
AdditionalSecurityCheckTest.class,
diff --git a/modules/core/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java b/modules/core/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
index 6b5f791..cfdd442 100644
--- a/modules/core/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
@@ -318,7 +318,7 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
assertContains(log, output, CommandHandler.UTILITY_NAME);
}
- checkHelp(output, "org.apache.ignite.util/control.sh_cache_help.output");
+ checkHelp(output, "org.apache.ignite.util/" + getClass().getSimpleName() + "_cache_help.output");
}
/** */
@@ -349,7 +349,7 @@ public class GridCommandHandlerClusterByClassTest extends GridCommandHandlerClus
assertNotContains(log, testOutStr, "Control.sh");
- checkHelp(testOutStr, "org.apache.ignite.util/control.sh_help.output");
+ checkHelp(testOutStr, "org.apache.ignite.util/" + getClass().getSimpleName() + "_help.output");
}
/**
diff --git a/modules/core/src/test/resources/org.apache.ignite.util/control.sh_cache_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_cache_help.output
similarity index 100%
copy from modules/core/src/test/resources/org.apache.ignite.util/control.sh_cache_help.output
copy to modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_cache_help.output
diff --git a/modules/core/src/test/resources/org.apache.ignite.util/control.sh_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
similarity index 100%
copy from modules/core/src/test/resources/org.apache.ignite.util/control.sh_help.output
copy to modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
diff --git a/modules/core/src/test/resources/org.apache.ignite.util/control.sh_cache_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_cache_help.output
similarity index 100%
rename from modules/core/src/test/resources/org.apache.ignite.util/control.sh_cache_help.output
rename to modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_cache_help.output
diff --git a/modules/core/src/test/resources/org.apache.ignite.util/control.sh_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
similarity index 98%
rename from modules/core/src/test/resources/org.apache.ignite.util/control.sh_help.output
rename to modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
index 1775583..c3c825f 100644
--- a/modules/core/src/test/resources/org.apache.ignite.util/control.sh_help.output
+++ b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
@@ -13,13 +13,13 @@ This utility can do the following commands:
control.(sh|bat) --activate
Deactivate cluster (deprecated. Use --set-state instead):
- control.(sh|bat) --deactivate [--force] [--yes]
+ control.(sh|bat) --deactivate [--yes]
Print current cluster state:
control.(sh|bat) --state
Change cluster state:
- control.(sh|bat) --set-state INACTIVE|ACTIVE|ACTIVE_READ_ONLY [--force] [--yes]
+ control.(sh|bat) --set-state INACTIVE|ACTIVE|ACTIVE_READ_ONLY [--yes]
Parameters:
ACTIVE - Activate cluster. Cache updates are allowed.
diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
index 20ada42..35cd4e0 100644
--- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
+++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java
@@ -28,6 +28,7 @@ import java.io.LineNumberReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
+import java.security.cert.X509Certificate;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
@@ -967,6 +968,11 @@ public class GridJettyRestHandler extends AbstractHandler {
restReq.command(cmd);
+ Object certs = req.getAttribute("javax.servlet.request.X509Certificate");
+
+ if (certs instanceof X509Certificate[])
+ restReq.certificates((X509Certificate[])certs);
+
// TODO: In IGNITE 3.0 we should check credentials only for AUTHENTICATE command.
if (!credentials(params, IGNITE_LOGIN, IGNITE_PASSWORD, restReq))
credentials(params, USER_PARAM, PWD_PARAM, restReq);
diff --git a/parent/pom.xml b/parent/pom.xml
index 80f12ef..d08a7ea 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -872,8 +872,7 @@
<exclude>src/main/java/org/jsr166/*.java</exclude>
<exclude>src/main/java/org/mindrot/*.java</exclude>
<exclude>src/test/java/org/apache/ignite/p2p/p2p.properties</exclude><!--test depends on file content-->
- <exclude>src/test/resources/org.apache.ignite.util/control.sh_cache_help.output</exclude><!--test depends on file content-->
- <exclude>src/test/resources/org.apache.ignite.util/control.sh_help.output</exclude><!--test depends on file content-->
+ <exclude>src/test/resources/org.apache.ignite.util/*.output</exclude><!--test depends on file content-->
<exclude>src/test/resources/log/ignite.log.tst</exclude><!--test resource-->
<exclude>src/test/java/org/apache/ignite/spi/deployment/uri/META-INF/ignite.incorrefs</exclude><!--test resource-->
<exclude>src/test/java/org/apache/ignite/spi/deployment/uri/META-INF/ignite.empty</exclude><!--should be empty-->