Repository: ignite
Updated Branches:
  refs/heads/master 5dc4de83f -> 61c9ffb7a


IGNITE-8076: Java Thin Client: authentication. This closes #3720.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/c1e7da15
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/c1e7da15
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/c1e7da15

Branch: refs/heads/master
Commit: c1e7da15cc188ad0addce5002b2868c5a6e4dc8c
Parents: f80d3a9
Author: Alexey Kukushkin <[email protected]>
Authored: Mon Apr 2 11:08:05 2018 +0300
Committer: devozerov <[email protected]>
Committed: Mon Apr 2 11:10:53 2018 +0300

----------------------------------------------------------------------
 .../main/java/org/apache/ignite/Ignition.java   |   4 +-
 .../client/ClientConfigurationException.java    |  51 -----
 .../internal/client/thin/ReliableChannel.java   |   7 +-
 .../internal/client/thin/TcpClientChannel.java  |  71 ++++--
 .../internal/client/thin/TcpIgniteClient.java   |   5 +-
 .../odbc/ClientListenerNioListener.java         |  15 +-
 .../client/ClientConnectionContext.java         |  54 ++++-
 .../platform/client/ClientRequestHandler.java   |  18 +-
 .../org/apache/ignite/client/SecurityTest.java  | 158 -------------
 .../org/apache/ignite/client/SecurityTest.java  | 228 +++++++++++++++++++
 .../Client/ClientConnectionTest.cs              |   5 +-
 11 files changed, 358 insertions(+), 258 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/c1e7da15/modules/core/src/main/java/org/apache/ignite/Ignition.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/Ignition.java 
b/modules/core/src/main/java/org/apache/ignite/Ignition.java
index 229dbdb..835896e 100644
--- a/modules/core/src/main/java/org/apache/ignite/Ignition.java
+++ b/modules/core/src/main/java/org/apache/ignite/Ignition.java
@@ -22,6 +22,7 @@ import java.net.URL;
 import java.util.List;
 import java.util.Objects;
 import java.util.UUID;
+import org.apache.ignite.client.ClientException;
 import org.apache.ignite.configuration.ClientConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgnitionEx;
@@ -29,7 +30,6 @@ import org.apache.ignite.internal.client.thin.TcpIgniteClient;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.spi.discovery.DiscoverySpi;
 import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
-import org.apache.ignite.client.ClientConfigurationException;
 import org.apache.ignite.client.IgniteClient;
 import org.apache.ignite.thread.IgniteThread;
 import org.jetbrains.annotations.Nullable;
@@ -584,7 +584,7 @@ public class Ignition {
      * @param cfg Thin client configuration.
      * @return Successfully opened thin client connection.
      */
-    public static IgniteClient startClient(ClientConfiguration cfg) throws 
ClientConfigurationException {
+    public static IgniteClient startClient(ClientConfiguration cfg) throws 
ClientException {
         Objects.requireNonNull(cfg, "cfg");
 
         return TcpIgniteClient.start(cfg);

http://git-wip-us.apache.org/repos/asf/ignite/blob/c1e7da15/modules/core/src/main/java/org/apache/ignite/client/ClientConfigurationException.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/client/ClientConfigurationException.java
 
b/modules/core/src/main/java/org/apache/ignite/client/ClientConfigurationException.java
deleted file mode 100644
index aac37eb..0000000
--- 
a/modules/core/src/main/java/org/apache/ignite/client/ClientConfigurationException.java
+++ /dev/null
@@ -1,51 +0,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.
- */
-
-package org.apache.ignite.client;
-
-/**
- * Indicates Ignite client configuration error.
- */
-public class ClientConfigurationException extends ClientException {
-    /** Serial version uid. */
-    private static final long serialVersionUID = 0L;
-
-    /**
-     * Default constructor.
-     */
-    public ClientConfigurationException() {
-    }
-
-    /**
-     * Constructs a new exception with the specified detail message.
-     *
-     * @param msg the detail message.
-     */
-    public ClientConfigurationException(String msg) {
-        super(msg);
-    }
-
-    /**
-     * Constructs a new exception with the specified cause and a detail
-     * message of <tt>(cause==null ? null : cause.toString())</tt>.
-     *
-     * @param cause the cause.
-     */
-    public ClientConfigurationException(Throwable cause) {
-        super(cause);
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/c1e7da15/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ReliableChannel.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ReliableChannel.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ReliableChannel.java
index 83a9bc5..392b8f8 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ReliableChannel.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/ReliableChannel.java
@@ -31,7 +31,6 @@ import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.client.ClientConfigurationException;
 import org.apache.ignite.client.ClientConnectionException;
 import org.apache.ignite.client.ClientException;
 import org.apache.ignite.configuration.ClientConfiguration;
@@ -68,7 +67,7 @@ final class ReliableChannel implements AutoCloseable {
     ReliableChannel(
         Function<ClientChannelConfiguration, Result<ClientChannel>> chFactory,
         ClientConfiguration clientCfg
-    ) throws ClientConfigurationException {
+    ) throws ClientException {
         if (chFactory == null)
             throw new NullPointerException("chFactory");
 
@@ -164,7 +163,7 @@ final class ReliableChannel implements AutoCloseable {
     /**
      * @return host:port_range address lines parsed as {@link 
InetSocketAddress}.
      */
-    private static List<InetSocketAddress> parseAddresses(String[] addrs) 
throws ClientConfigurationException {
+    private static List<InetSocketAddress> parseAddresses(String[] addrs) 
throws ClientException {
         Collection<HostAndPortRange> ranges = new ArrayList<>(addrs.length);
 
         for (String a : addrs) {
@@ -177,7 +176,7 @@ final class ReliableChannel implements AutoCloseable {
                 ));
             }
             catch (IgniteCheckedException e) {
-                throw new ClientConfigurationException(e);
+                throw new ClientException(e);
             }
         }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/c1e7da15/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpClientChannel.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpClientChannel.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpClientChannel.java
index e462903..404793a 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpClientChannel.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpClientChannel.java
@@ -32,6 +32,8 @@ import java.security.NoSuchAlgorithmException;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
@@ -47,6 +49,11 @@ import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.X509TrustManager;
+import org.apache.ignite.client.ClientAuthenticationException;
+import org.apache.ignite.client.ClientConnectionException;
+import org.apache.ignite.client.SslMode;
+import org.apache.ignite.client.SslProtocol;
+import org.apache.ignite.configuration.ClientConfiguration;
 import org.apache.ignite.internal.binary.BinaryRawWriterEx;
 import org.apache.ignite.internal.binary.BinaryReaderExImpl;
 import org.apache.ignite.internal.binary.BinaryWriterExImpl;
@@ -55,16 +62,23 @@ import 
org.apache.ignite.internal.binary.streams.BinaryHeapOutputStream;
 import org.apache.ignite.internal.binary.streams.BinaryInputStream;
 import org.apache.ignite.internal.binary.streams.BinaryOffheapOutputStream;
 import org.apache.ignite.internal.binary.streams.BinaryOutputStream;
-import org.apache.ignite.client.ClientAuthenticationException;
-import org.apache.ignite.configuration.ClientConfiguration;
-import org.apache.ignite.client.ClientConnectionException;
-import org.apache.ignite.client.SslMode;
-import org.apache.ignite.client.SslProtocol;
 
 /**
  * Implements {@link ClientChannel} over TCP.
  */
 class TcpClientChannel implements ClientChannel {
+    /** Protocol version: 1.1.0. */
+    private static final ProtocolVersion V1_1_0 = new 
ProtocolVersion((short)1, (short)1, (short)0);
+
+    /** Protocol version 1 0 0. */
+    private static final ProtocolVersion V1_0_0 = new 
ProtocolVersion((short)1, (short)0, (short)0);
+
+    /** Supported protocol versions. */
+    private static final Collection<ProtocolVersion> supportedVers = 
Arrays.asList(V1_1_0, V1_0_0);
+
+    /** Protocol version agreed with the server. */
+    private ProtocolVersion ver = V1_1_0;
+
     /** Channel. */
     private final Socket sock;
 
@@ -74,9 +88,6 @@ class TcpClientChannel implements ClientChannel {
     /** Input stream. */
     private final InputStream in;
 
-    /** Version. */
-    private final ProtocolVersion ver = new ProtocolVersion((short)1, 
(short)0, (short)0);
-
     /** Request id. */
     private final AtomicLong reqId = new AtomicLong(1);
 
@@ -213,7 +224,7 @@ class TcpClientChannel implements ClientChannel {
     private void handshake(String user, String pwd)
         throws ClientConnectionException, ClientAuthenticationException {
         handshakeReq(user, pwd);
-        handshakeRes();
+        handshakeRes(user, pwd);
     }
 
     /** Send handshake request. */
@@ -226,7 +237,7 @@ class TcpClientChannel implements ClientChannel {
             req.writeShort(ver.patch());
             req.writeByte((byte)2); // client code, always 2
 
-            if (user != null && user.length() > 0) {
+            if (ver.compareTo(V1_1_0) >= 0 && user != null && user.length() > 
0) {
                 req.writeByteArray(marshalString(user));
                 req.writeByteArray(marshalString(pwd));
             }
@@ -238,7 +249,8 @@ class TcpClientChannel implements ClientChannel {
     }
 
     /** Receive and handle handshake response. */
-    private void handshakeRes() throws ClientConnectionException, 
ClientAuthenticationException {
+    private void handshakeRes(String user, String pwd)
+        throws ClientConnectionException, ClientAuthenticationException {
         int resSize = new BinaryHeapInputStream(read(4)).readInt();
 
         if (resSize <= 0)
@@ -249,17 +261,32 @@ class TcpClientChannel implements ClientChannel {
         if (!res.readBoolean()) { // success flag
             ProtocolVersion srvVer = new ProtocolVersion(res.readShort(), 
res.readShort(), res.readShort());
 
-            String err = new BinaryReaderExImpl(null, res, null, 
true).readString();
-
-            if (err != null && 
err.toUpperCase().matches(".*USER.*INCORRECT.*"))
-                throw new ClientAuthenticationException();
-            else
-                throw new ClientProtocolError(String.format(
-                    "Protocol version mismatch: client %s / server %s. Server 
details: %s",
-                    ver,
-                    srvVer,
-                    err
-                ));
+            try (BinaryReaderExImpl r = new BinaryReaderExImpl(null, res, 
null, true)) {
+                String err = r.readString();
+
+                if (err != null && 
err.toUpperCase().matches(".*USER.*INCORRECT.*"))
+                    throw new ClientAuthenticationException();
+                else if (ver.equals(srvVer))
+                    throw new ClientProtocolError(err);
+                else if (!supportedVers.contains(srvVer) ||
+                    (srvVer.compareTo(V1_1_0) < 0 && user != null && 
user.length() > 0))
+                    // Server version is not supported by this client OR 
server version is less than 1.1.0 supporting
+                    // authentication and authentication is required.
+                    throw new ClientProtocolError(String.format(
+                        "Protocol version mismatch: client %s / server %s. 
Server details: %s",
+                        ver,
+                        srvVer,
+                        err
+                    ));
+                else { // retry with server version
+                    ver = srvVer;
+
+                    handshake(user, pwd);
+                }
+            }
+            catch (IOException e) {
+                throw new ClientConnectionException(e);
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/c1e7da15/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpIgniteClient.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpIgniteClient.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpIgniteClient.java
index 99d05df..7beeb79 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpIgniteClient.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/client/thin/TcpIgniteClient.java
@@ -33,7 +33,6 @@ import org.apache.ignite.cache.query.FieldsQueryCursor;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.client.ClientCache;
 import org.apache.ignite.client.ClientCacheConfiguration;
-import org.apache.ignite.client.ClientConfigurationException;
 import org.apache.ignite.client.ClientException;
 import org.apache.ignite.client.IgniteClient;
 import org.apache.ignite.configuration.ClientConfiguration;
@@ -73,7 +72,7 @@ public class TcpIgniteClient implements IgniteClient {
      * Private constructor. Use {@link 
TcpIgniteClient#start(ClientConfiguration)} to create an instance of
      * {@link TcpClientChannel}.
      */
-    private TcpIgniteClient(ClientConfiguration cfg) throws 
ClientConfigurationException {
+    private TcpIgniteClient(ClientConfiguration cfg) throws ClientException {
         Function<ClientChannelConfiguration, Result<ClientChannel>> chFactory 
= chCfg -> {
             try {
                 return new Result<>(new TcpClientChannel(chCfg));
@@ -189,7 +188,7 @@ public class TcpIgniteClient implements IgniteClient {
      * @param cfg Thin client configuration.
      * @return Successfully opened thin client connection.
      */
-    public static IgniteClient start(ClientConfiguration cfg) throws 
ClientConfigurationException {
+    public static IgniteClient start(ClientConfiguration cfg) throws 
ClientException {
         return new TcpIgniteClient(cfg);
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/c1e7da15/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
----------------------------------------------------------------------
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 f472a9f..53b14d7 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
@@ -33,6 +33,7 @@ import org.apache.ignite.internal.util.GridSpinBusyLock;
 import org.apache.ignite.internal.util.nio.GridNioServerListenerAdapter;
 import org.apache.ignite.internal.util.nio.GridNioSession;
 import org.apache.ignite.internal.util.nio.GridNioSessionMetaKey;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -87,6 +88,7 @@ public class ClientListenerNioListener extends 
GridNioServerListenerAdapter<byte
         this.busyLock = busyLock;
         this.maxCursors = cliConnCfg.getMaxOpenCursorsPerConnection();
         this.cliConnCfg = cliConnCfg;
+
         log = ctx.log(getClass());
     }
 
@@ -134,7 +136,7 @@ public class ClientListenerNioListener extends 
GridNioServerListenerAdapter<byte
             req = parser.decode(msg);
         }
         catch (Exception e) {
-            log.error("Failed to parse client request.", e);
+            U.error(log, "Failed to parse client request.", e);
 
             ses.close();
 
@@ -167,7 +169,7 @@ public class ClientListenerNioListener extends 
GridNioServerListenerAdapter<byte
             ses.send(outMsg);
         }
         catch (Exception e) {
-            log.error("Failed to process client request [req=" + req + ']', e);
+            U.error(log, "Failed to process client request [req=" + req + ']', 
e);
 
             ses.send(parser.encode(handler.handleException(e, req)));
         }
@@ -192,7 +194,7 @@ public class ClientListenerNioListener extends 
GridNioServerListenerAdapter<byte
         byte cmd = reader.readByte();
 
         if (cmd != ClientListenerRequest.HANDSHAKE) {
-            log.error("Unexpected client request (will close session): " + 
ses.remoteAddress());
+            U.warn(log, "Unexpected client request (will close session): " + 
ses.remoteAddress());
 
             ses.close();
 
@@ -221,16 +223,13 @@ public class ClientListenerNioListener extends 
GridNioServerListenerAdapter<byte
 
                 ses.addMeta(CONN_CTX_META_KEY, connCtx);
             }
-            else {
-                log.warning("Unsupported version: " + ver.toString());
-
+            else
                 throw new IgniteCheckedException("Unsupported version.");
-            }
 
             connCtx.handler().writeHandshake(writer);
         }
         catch (IgniteCheckedException e) {
-            log.error("Error on handshake. " + e.getMessage(), e);
+            U.warn(log, "Error during handshake [rmtAddr=" + 
ses.remoteAddress() + ", msg=" + e.getMessage() + ']');
 
             ClientListenerProtocolVersion currVer;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/c1e7da15/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientConnectionContext.java
----------------------------------------------------------------------
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 2d81f35..7ab2d33 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
@@ -17,8 +17,13 @@
 
 package org.apache.ignite.internal.processors.platform.client;
 
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.binary.BinaryReaderExImpl;
+import 
org.apache.ignite.internal.processors.authentication.AuthorizationContext;
 import 
org.apache.ignite.internal.processors.odbc.ClientListenerConnectionContext;
 import org.apache.ignite.internal.processors.odbc.ClientListenerMessageParser;
 import 
org.apache.ignite.internal.processors.odbc.ClientListenerProtocolVersion;
@@ -33,11 +38,17 @@ public class ClientConnectionContext implements 
ClientListenerConnectionContext
     /** Version 1.0.0. */
     private static final ClientListenerProtocolVersion VER_1_0_0 = 
ClientListenerProtocolVersion.create(1, 0, 0);
 
+    /** Version 1.1.0. */
+    private static final ClientListenerProtocolVersion VER_1_1_0 = 
ClientListenerProtocolVersion.create(1, 1, 0);
+
+    /** Supported versions. */
+    private static final Collection<ClientListenerProtocolVersion> 
SUPPORTED_VERS = Arrays.asList(VER_1_1_0, VER_1_0_0);
+
     /** Message parser. */
     private final ClientMessageParser parser;
 
     /** Request handler. */
-    private final ClientRequestHandler handler;
+    private ClientRequestHandler handler;
 
     /** Handle registry. */
     private final ClientResourceRegistry resReg = new ClientResourceRegistry();
@@ -63,7 +74,7 @@ public class ClientConnectionContext implements 
ClientListenerConnectionContext
         kernalCtx = ctx;
 
         parser = new ClientMessageParser(ctx);
-        handler = new ClientRequestHandler(this);
+
         this.maxCursors = maxCursors;
     }
 
@@ -87,17 +98,48 @@ public class ClientConnectionContext implements 
ClientListenerConnectionContext
 
     /** {@inheritDoc} */
     @Override public boolean isVersionSupported(ClientListenerProtocolVersion 
ver) {
-        return VER_1_0_0.equals(ver);
+        return SUPPORTED_VERS.contains(ver);
     }
 
     /** {@inheritDoc} */
     @Override public ClientListenerProtocolVersion currentVersion() {
-        return VER_1_0_0;
+        return VER_1_1_0;
     }
 
     /** {@inheritDoc} */
-    @Override public void 
initializeFromHandshake(ClientListenerProtocolVersion ver, BinaryReaderExImpl 
reader) {
-        // No-op.
+    @Override public void 
initializeFromHandshake(ClientListenerProtocolVersion ver, BinaryReaderExImpl 
reader)
+        throws IgniteCheckedException {
+        boolean hasMore;
+
+        String user = null;
+        String pwd = null;
+        AuthorizationContext authCtx = null;
+
+        if (ver.compareTo(VER_1_1_0) >= 0) {
+            try {
+                hasMore = reader.available() > 0;
+            }
+            catch (IOException e) {
+                throw new IgniteCheckedException("Handshake error: " + 
e.getMessage(), e);
+            }
+
+            if (hasMore) {
+                user = reader.readString();
+                pwd = reader.readString();
+            }
+        }
+
+        if (kernalCtx.authentication().enabled()) {
+            if (user == null || user.length() == 0)
+                throw new IgniteCheckedException("Unauthenticated sessions are 
prohibited.");
+
+            authCtx = kernalCtx.authentication().authenticate(user, pwd);
+
+            if (authCtx == null)
+                throw new IgniteCheckedException("Unknown authentication 
error.");
+        }
+
+        handler = new ClientRequestHandler(this, authCtx);
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/c1e7da15/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java
index 3f6c082..faa50bc 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/client/ClientRequestHandler.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.processors.platform.client;
 
 import org.apache.ignite.internal.binary.BinaryWriterExImpl;
+import 
org.apache.ignite.internal.processors.authentication.AuthorizationContext;
 import org.apache.ignite.internal.processors.odbc.ClientListenerRequest;
 import org.apache.ignite.internal.processors.odbc.ClientListenerRequestHandler;
 import org.apache.ignite.internal.processors.odbc.ClientListenerResponse;
@@ -29,20 +30,33 @@ public class ClientRequestHandler implements 
ClientListenerRequestHandler {
     /** Client context. */
     private final ClientConnectionContext ctx;
 
+    /** Auth context. */
+    private final AuthorizationContext authCtx;
+
     /**
      * Constructor.
      *
      * @param ctx Kernal context.
      */
-    ClientRequestHandler(ClientConnectionContext ctx) {
+    ClientRequestHandler(ClientConnectionContext ctx, AuthorizationContext 
authCtx) {
         assert ctx != null;
 
         this.ctx = ctx;
+        this.authCtx = authCtx;
     }
 
     /** {@inheritDoc} */
     @Override public ClientListenerResponse handle(ClientListenerRequest req) {
-        return ((ClientRequest) req).process(ctx);
+        if (authCtx != null)
+            AuthorizationContext.context(authCtx);
+
+        try {
+            return ((ClientRequest)req).process(ctx);
+        }
+        finally {
+            if (authCtx != null)
+                AuthorizationContext.clear();
+        }
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/c1e7da15/modules/core/src/test/java/org/apache/ignite/client/SecurityTest.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/test/java/org/apache/ignite/client/SecurityTest.java 
b/modules/core/src/test/java/org/apache/ignite/client/SecurityTest.java
deleted file mode 100644
index 603ac6c..0000000
--- a/modules/core/src/test/java/org/apache/ignite/client/SecurityTest.java
+++ /dev/null
@@ -1,158 +0,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.
- */
-
-package org.apache.ignite.client;
-
-import java.util.AbstractMap.SimpleEntry;
-import java.util.function.Function;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.Ignition;
-import org.apache.ignite.configuration.ClientConfiguration;
-import org.apache.ignite.configuration.ClientConnectorConfiguration;
-import org.apache.ignite.configuration.DataRegionConfiguration;
-import org.apache.ignite.configuration.DataStorageConfiguration;
-import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.internal.IgniteEx;
-import 
org.apache.ignite.internal.processors.authentication.AuthorizationContext;
-import org.apache.ignite.ssl.SslContextFactory;
-import org.junit.Test;
-
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-/**
- * Thin client security test.
- */
-public class SecurityTest {
-    /** Test SSL/TLS encryption. */
-    @Test
-    public void testEncryption() throws Exception {
-        // Server-side security configuration
-        IgniteConfiguration srvCfg = Config.getServerConfiguration();
-
-        SslContextFactory sslCfg = new SslContextFactory();
-
-        Function<String, String> rsrcPath = rsrc -> 
SecurityTest.class.getResource(rsrc).getPath();
-
-        sslCfg.setKeyStoreFilePath(rsrcPath.apply("/server.jks"));
-        sslCfg.setKeyStorePassword("123456".toCharArray());
-        sslCfg.setTrustStoreFilePath(rsrcPath.apply("/trust.jks"));
-        sslCfg.setTrustStorePassword("123456".toCharArray());
-
-        srvCfg.setClientConnectorConfiguration(new 
ClientConnectorConfiguration()
-            .setSslEnabled(true)
-            .setSslClientAuth(true)
-        );
-
-        srvCfg.setSslContextFactory(sslCfg);
-
-        // Client-side security configuration
-        ClientConfiguration clientCfg = new 
ClientConfiguration().setAddresses(Config.SERVER);
-
-        try (Ignite ignored = Ignition.start(srvCfg)) {
-            boolean failed;
-
-            try (IgniteClient client = Ignition.startClient(clientCfg)) {
-                client.<Integer, 
String>cache(Config.DEFAULT_CACHE_NAME).put(1, "1");
-
-                failed = false;
-            }
-            catch (Exception ex) {
-                failed = true;
-            }
-
-            assertTrue("Client connection without SSL must fail", failed);
-
-            // Not using user-supplied SSL Context Factory:
-            try (IgniteClient client = Ignition.startClient(clientCfg
-                .setSslMode(SslMode.REQUIRED)
-                
.setSslClientCertificateKeyStorePath(rsrcPath.apply("/client.jks"))
-                .setSslClientCertificateKeyStoreType("JKS")
-                .setSslClientCertificateKeyStorePassword("123456")
-                
.setSslTrustCertificateKeyStorePath(rsrcPath.apply("/trust.jks"))
-                .setSslTrustCertificateKeyStoreType("JKS")
-                .setSslTrustCertificateKeyStorePassword("123456")
-                .setSslKeyAlgorithm("SunX509")
-                .setSslTrustAll(false)
-                .setSslProtocol(SslProtocol.TLS)
-            )) {
-                client.<Integer, 
String>cache(Config.DEFAULT_CACHE_NAME).put(1, "1");
-            }
-
-            // Using user-supplied SSL Context Factory
-            try (IgniteClient client = Ignition.startClient(clientCfg
-                .setSslMode(SslMode.REQUIRED)
-                .setSslContextFactory(sslCfg)
-            )) {
-                client.<Integer, 
String>cache(Config.DEFAULT_CACHE_NAME).put(1, "1");
-            }
-        }
-    }
-
-    /** Test authentication. */
-    @Test
-    public void testAuthentication() throws IgniteCheckedException {
-        try (Ignite ignite = Ignition.start(Config.getServerConfiguration()
-            .setAuthenticationEnabled(true)
-            .setDataStorageConfiguration(new DataStorageConfiguration()
-                .setDefaultDataRegionConfiguration(new 
DataRegionConfiguration().setPersistenceEnabled(true))
-            )
-        )) {
-            ignite.cluster().active(true);
-
-            Function<SimpleEntry<String, String>, Exception> authenticate = 
cred -> {
-                ClientConfiguration clientCfg = new 
ClientConfiguration().setAddresses(Config.SERVER)
-                    .setUserName(cred.getKey())
-                    .setUserPassword(cred.getValue());
-
-                try (IgniteClient client = Ignition.startClient(clientCfg)) {
-                    client.getOrCreateCache("testAuthentication");
-                }
-                catch (Exception e) {
-                    return e;
-                }
-
-                return null;
-            };
-
-            assertTrue(
-                "Authentication with invalid credentials succeeded",
-                authenticate.apply(new SimpleEntry<>("bad-user", 
"bad-password"))
-                    instanceof ClientAuthenticationException
-            );
-
-            SimpleEntry<String, String> validCred = new SimpleEntry<>("user", 
"password");
-
-            IgniteEx igniteEx = (IgniteEx)ignite;
-
-            
AuthorizationContext.context(igniteEx.context().authentication().authenticate("ignite",
 "ignite"));
-
-            try {
-                
igniteEx.context().authentication().addUser(validCred.getKey(), 
validCred.getValue());
-            }
-            catch (IgniteCheckedException ignore) {
-                // Ignore "user already exists" exception
-            }
-
-            assertNull(
-                "Authentication with valid credentials failed",
-                authenticate.apply(validCred)
-            );
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/c1e7da15/modules/indexing/src/test/java/org/apache/ignite/client/SecurityTest.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/test/java/org/apache/ignite/client/SecurityTest.java 
b/modules/indexing/src/test/java/org/apache/ignite/client/SecurityTest.java
new file mode 100644
index 0000000..dc57f0c
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/client/SecurityTest.java
@@ -0,0 +1,228 @@
+/*
+ * 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.client;
+
+import java.nio.file.Paths;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.function.Function;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.ClientConfiguration;
+import org.apache.ignite.configuration.ClientConnectorConfiguration;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.ssl.SslContextFactory;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Thin client security test.
+ */
+public class SecurityTest {
+    /** Ignite home. */
+    private static final String IGNITE_HOME = U.getIgniteHome();
+
+    /**
+     * Setup before each test.
+     */
+    @Before
+    public void beforeEach() throws IgniteCheckedException {
+        U.resolveWorkDirectory(U.defaultWorkDirectory(), "db", true);
+    }
+
+    /** Test SSL/TLS encryption. */
+    @Test
+    public void testEncryption() throws Exception {
+        // Server-side security configuration
+        IgniteConfiguration srvCfg = Config.getServerConfiguration();
+
+        SslContextFactory sslCfg = new SslContextFactory();
+
+        Function<String, String> rsrcPath = rsrc -> Paths.get(
+            IGNITE_HOME == null ? "." : IGNITE_HOME,
+            "modules",
+            "core",
+            "src",
+            "test",
+            "resources",
+            rsrc
+        ).toString();
+
+        sslCfg.setKeyStoreFilePath(rsrcPath.apply("/server.jks"));
+        sslCfg.setKeyStorePassword("123456".toCharArray());
+        sslCfg.setTrustStoreFilePath(rsrcPath.apply("/trust.jks"));
+        sslCfg.setTrustStorePassword("123456".toCharArray());
+
+        srvCfg.setClientConnectorConfiguration(new 
ClientConnectorConfiguration()
+            .setSslEnabled(true)
+            .setSslClientAuth(true)
+        );
+
+        srvCfg.setSslContextFactory(sslCfg);
+
+        // Client-side security configuration
+        ClientConfiguration clientCfg = new 
ClientConfiguration().setAddresses(Config.SERVER);
+
+        try (Ignite ignored = Ignition.start(srvCfg)) {
+            boolean failed;
+
+            try (IgniteClient client = Ignition.startClient(clientCfg)) {
+                client.<Integer, 
String>cache(Config.DEFAULT_CACHE_NAME).put(1, "1");
+
+                failed = false;
+            }
+            catch (Exception ex) {
+                failed = true;
+            }
+
+            assertTrue("Client connection without SSL must fail", failed);
+
+            // Not using user-supplied SSL Context Factory:
+            try (IgniteClient client = Ignition.startClient(clientCfg
+                .setSslMode(SslMode.REQUIRED)
+                
.setSslClientCertificateKeyStorePath(rsrcPath.apply("/client.jks"))
+                .setSslClientCertificateKeyStoreType("JKS")
+                .setSslClientCertificateKeyStorePassword("123456")
+                
.setSslTrustCertificateKeyStorePath(rsrcPath.apply("/trust.jks"))
+                .setSslTrustCertificateKeyStoreType("JKS")
+                .setSslTrustCertificateKeyStorePassword("123456")
+                .setSslKeyAlgorithm("SunX509")
+                .setSslTrustAll(false)
+                .setSslProtocol(SslProtocol.TLS)
+            )) {
+                client.<Integer, 
String>cache(Config.DEFAULT_CACHE_NAME).put(1, "1");
+            }
+
+            // Using user-supplied SSL Context Factory
+            try (IgniteClient client = Ignition.startClient(clientCfg
+                .setSslMode(SslMode.REQUIRED)
+                .setSslContextFactory(sslCfg)
+            )) {
+                client.<Integer, 
String>cache(Config.DEFAULT_CACHE_NAME).put(1, "1");
+            }
+        }
+    }
+
+    /** Test valid user authentication. */
+    @Test
+    public void testInvalidUserAuthentication() throws Exception {
+        try (Ignite ignored = igniteWithAuthentication();
+             IgniteClient client = Ignition.startClient(new 
ClientConfiguration().setAddresses(Config.SERVER)
+                 .setUserName("JOE")
+                 .setUserPassword("password")
+             )
+        ) {
+            Exception authError = null;
+
+            try {
+                client.getOrCreateCache("testAuthentication");
+            }
+            catch (Exception e) {
+                authError = e;
+            }
+
+            assertNotNull("Authentication with invalid credentials succeeded", 
authError);
+            assertTrue("Invalid type of authentication error", authError 
instanceof ClientAuthenticationException);
+        }
+    }
+
+    /** Test valid user authentication. */
+    @Test
+    public void testValidUserAuthentication() throws Exception {
+        final String USER = "joe";
+        final String PWD = "password";
+
+        try (Ignite ignored = igniteWithAuthentication(new SimpleEntry<>(USER, 
PWD));
+             IgniteClient client = Ignition.startClient(new 
ClientConfiguration().setAddresses(Config.SERVER)
+                 .setUserName(USER)
+                 .setUserPassword(PWD)
+             )
+        ) {
+            client.getOrCreateCache("testAuthentication");
+        }
+    }
+
+    /** Test user cannot create user. */
+    @Test
+    public void testUserCannotCreateUser() throws Exception {
+        final String USER = "joe";
+        final String PWD = "password";
+
+        try (Ignite ignored = igniteWithAuthentication(new SimpleEntry<>(USER, 
PWD));
+             IgniteClient client = Ignition.startClient(new 
ClientConfiguration().setAddresses(Config.SERVER)
+                 .setUserName(USER)
+                 .setUserPassword(PWD)
+             )
+        ) {
+            Exception authError = null;
+
+            try {
+                client.query(
+                    new SqlFieldsQuery(String.format("CREATE USER \"%s\" WITH 
PASSWORD '%s'", "joe2", "password"))
+                ).getAll();
+            }
+            catch (Exception e) {
+                authError = e;
+            }
+
+            assertNotNull("User created another user", authError);
+        }
+    }
+
+    /**
+     * @return Ignite configuration with authentication enabled
+     */
+    @SafeVarargs
+    private static Ignite igniteWithAuthentication(SimpleEntry<String, 
String>... users) throws Exception {
+        Ignite ignite = Ignition.start(Config.getServerConfiguration()
+            .setAuthenticationEnabled(true)
+            .setDataStorageConfiguration(new DataStorageConfiguration()
+                .setDefaultDataRegionConfiguration(new 
DataRegionConfiguration().setPersistenceEnabled(true))
+            )
+        );
+
+        ignite.cluster().active(true);
+
+        for (SimpleEntry<String, String> u : users)
+            createUser(u.getKey(), u.getValue());
+
+        return ignite;
+    }
+
+    /**
+     * Create user.
+     */
+    private static void createUser(String user, String pwd) throws Exception {
+        try (IgniteClient client = Ignition.startClient(new 
ClientConfiguration()
+            .setAddresses(Config.SERVER)
+            .setUserName("ignite")
+            .setUserPassword("ignite")
+        )) {
+            client.query(
+                new SqlFieldsQuery(String.format("CREATE USER \"%s\" WITH 
PASSWORD '%s'", user, pwd))
+            ).getAll();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/c1e7da15/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs
index 2169edc..9da9a03 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs
@@ -22,6 +22,7 @@ namespace Apache.Ignite.Core.Tests.Client
     using System.Linq;
     using System.Net;
     using System.Net.Sockets;
+    using System.Text.RegularExpressions;
     using System.Threading;
     using System.Threading.Tasks;
     using Apache.Ignite.Core.Client;
@@ -130,8 +131,8 @@ namespace Apache.Ignite.Core.Tests.Client
 
                 Assert.AreEqual(ClientStatusCode.Fail, ex.StatusCode);
 
-                Assert.AreEqual("Client handshake failed: 'Unsupported 
version.'. " +
-                                "Client version: -1.-1.-1. Server version: 
1.0.0", ex.Message);
+                Assert.IsTrue(Regex.IsMatch(ex.Message, "Client handshake 
failed: 'Unsupported version.'. " +
+                                "Client version: -1.-1.-1. Server version: 
[0-9]+.[0-9]+.[0-9]+"));
             }
         }
 

Reply via email to