This is an automated email from the ASF dual-hosted git repository.

ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 797084781f IGNITE-19073 Add authentication to thin client protocol 
(#1918)
797084781f is described below

commit 797084781f5a0e34cb28e20ba694d772607e9642
Author: Ivan Gagarkin <gagarkin....@gmail.com>
AuthorDate: Wed Apr 12 11:03:45 2023 +0400

    IGNITE-19073 Add authentication to thin client protocol (#1918)
    
    * Pass credentials in client handshake extensions, authenticate when 
required.
    * Disconnect clients when authentication configuration changes.
    
    Co-authored-by: Ivan Gagarkin <igagar...@gridgain.com>
---
 .../internal/client/proto/HandshakeExtension.java  |  60 +++++++++
 modules/client-handler/build.gradle                |   2 +
 .../client/handler/ItClientHandlerMetricsTest.java |  13 +-
 .../ignite/client/handler/ItClientHandlerTest.java | 146 ++++++++++++++++++++-
 .../client/handler/ItSslClientHandlerTest.java     |   6 +-
 .../apache/ignite/client/handler/TestServer.java   |  28 +++-
 .../ignite/client/handler/ClientContext.java       |  15 ++-
 .../ignite/client/handler/ClientHandlerModule.java |  46 +++++--
 .../handler/ClientInboundMessageHandler.java       |  72 +++++++++-
 modules/client/build.gradle                        |   1 +
 .../apache/ignite/client/AbstractClientTest.java   |   2 +-
 .../apache/ignite/client/ClientComputeTest.java    |   6 +-
 .../apache/ignite/client/ClientMetricsTest.java    |  48 ++++++-
 .../apache/ignite/client/ConfigurationTest.java    |   2 +-
 .../org/apache/ignite/client/ConnectionTest.java   |   2 +-
 .../org/apache/ignite/client/HeartbeatTest.java    |   2 +-
 .../org/apache/ignite/client/MultiClusterTest.java |   6 +-
 .../org/apache/ignite/client/RetryPolicyTest.java  |   2 +-
 .../ignite/client/TestClientHandlerModule.java     |  19 ++-
 .../java/org/apache/ignite/client/TestServer.java  |  35 ++++-
 .../org/apache/ignite/internal/app/IgniteImpl.java |  12 +-
 21 files changed, 466 insertions(+), 59 deletions(-)

diff --git 
a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/HandshakeExtension.java
 
b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/HandshakeExtension.java
new file mode 100644
index 0000000000..34397328b6
--- /dev/null
+++ 
b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/HandshakeExtension.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.client.proto;
+
+import org.jetbrains.annotations.Nullable;
+
+/** Handshake extensions. */
+public enum HandshakeExtension {
+    USERNAME("username", String.class),
+    PASSWORD("password", String.class),
+    ;
+
+    private final String key;
+
+    private final Class<?> valueType;
+
+    HandshakeExtension(String key, Class<?> valueType) {
+        this.key = key;
+        this.valueType = valueType;
+    }
+
+    /**
+     * Finds an extension with a provided key.
+     *
+     * @param key Key.
+     * @return Extension.
+     */
+    @Nullable
+    public static HandshakeExtension fromKey(String key) {
+        for (HandshakeExtension e : values()) {
+            if (e.key.equalsIgnoreCase(key)) {
+                return e;
+            }
+        }
+        return null;
+    }
+
+    public String key() {
+        return key;
+    }
+
+    public Class<?> valueType() {
+        return valueType;
+    }
+}
diff --git a/modules/client-handler/build.gradle 
b/modules/client-handler/build.gradle
index 4765a447ee..7276adfda4 100644
--- a/modules/client-handler/build.gradle
+++ b/modules/client-handler/build.gradle
@@ -33,6 +33,7 @@ dependencies {
     implementation project(':ignite-network')
     implementation project(':ignite-core')
     implementation project(':ignite-schema')
+    implementation project(':ignite-security')
     implementation project(':ignite-metrics')
     implementation libs.jetbrains.annotations
     implementation libs.fastutil.core
@@ -56,6 +57,7 @@ dependencies {
     integrationTestImplementation project(':ignite-sql-engine')
     integrationTestImplementation project(':ignite-table')
     integrationTestImplementation project(':ignite-metrics')
+    integrationTestImplementation project(':ignite-security')
     
integrationTestImplementation(testFixtures(project(':ignite-configuration')))
     integrationTestImplementation(testFixtures(project(':ignite-core')))
     integrationTestImplementation libs.msgpack.core
diff --git 
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerMetricsTest.java
 
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerMetricsTest.java
index d524c9a5c1..3e24fe65da 100644
--- 
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerMetricsTest.java
+++ 
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerMetricsTest.java
@@ -54,7 +54,8 @@ public class ItClientHandlerMetricsTest {
                 TestSslConfig.builder()
                         
.keyStorePath(ItClientHandlerTestUtils.generateKeystore(workDir))
                         .keyStorePassword("changeit")
-                        .build()
+                        .build(),
+                null
         );
 
         var serverModule = testServer.start(testInfo);
@@ -68,7 +69,7 @@ public class ItClientHandlerMetricsTest {
 
     @Test
     void testSessionsRejected(TestInfo testInfo) throws Exception {
-        testServer = new TestServer(null);
+        testServer = new TestServer(null, null);
         var serverModule = testServer.start(testInfo);
 
         // Bad MAGIC.
@@ -89,7 +90,7 @@ public class ItClientHandlerMetricsTest {
 
     @Test
     void testSessionsRejectedTimeout(TestInfo testInfo) throws Exception {
-        testServer = new TestServer(null);
+        testServer = new TestServer(null, null);
         testServer.idleTimeout(300);
         var serverModule = testServer.start(testInfo);
 
@@ -106,7 +107,7 @@ public class ItClientHandlerMetricsTest {
 
     @Test
     void testSessionsAccepted(TestInfo testInfo) throws Exception {
-        testServer = new TestServer(null);
+        testServer = new TestServer(null, null);
         var serverModule = testServer.start(testInfo);
 
         ItClientHandlerTestUtils.connectAndHandshake(serverModule);
@@ -115,7 +116,7 @@ public class ItClientHandlerMetricsTest {
 
     @Test
     void testSessionsActive(TestInfo testInfo) throws Exception {
-        testServer = new TestServer(null);
+        testServer = new TestServer(null, null);
         var serverModule = testServer.start(testInfo);
 
         try (var ignored = 
ItClientHandlerTestUtils.connectAndHandshakeAndGetSocket(serverModule)) {
@@ -127,7 +128,7 @@ public class ItClientHandlerMetricsTest {
 
     @Test
     void testBytesSentReceived(TestInfo testInfo) throws Exception {
-        testServer = new TestServer(null);
+        testServer = new TestServer(null, null);
         var serverModule = testServer.start(testInfo);
 
         assertEquals(0, testServer.metrics().bytesSent());
diff --git 
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
 
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
index 2eecc295bb..8ff55202af 100644
--- 
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
+++ 
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.client.handler;
 
 import static org.apache.ignite.client.handler.ItClientHandlerTestUtils.MAGIC;
+import static 
org.apache.ignite.lang.ErrorGroups.Authentication.COMMON_AUTHENTICATION_ERR;
 import static 
org.apache.ignite.lang.ErrorGroups.Client.PROTOCOL_COMPATIBILITY_ERR;
 import static org.apache.ignite.lang.ErrorGroups.Common.UNKNOWN_ERR;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -31,15 +32,21 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.net.Socket;
+import org.apache.ignite.internal.configuration.AuthenticationConfiguration;
+import 
org.apache.ignite.internal.configuration.BasicAuthenticationProviderChange;
+import 
org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
+import 
org.apache.ignite.internal.configuration.testframework.InjectConfiguration;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.api.extension.ExtendWith;
 import org.msgpack.core.MessagePack;
 
 /**
  * Client connector integration tests with real sockets.
  */
+@ExtendWith(ConfigurationExtension.class)
 public class ItClientHandlerTest {
     private ClientHandlerModule serverModule;
 
@@ -47,9 +54,12 @@ public class ItClientHandlerTest {
 
     private int serverPort;
 
+    @InjectConfiguration
+    private AuthenticationConfiguration authenticationConfiguration;
+
     @BeforeEach
     public void setUp(TestInfo testInfo) {
-        testServer = new TestServer();
+        testServer = new TestServer(null, authenticationConfiguration);
         serverModule = testServer.start(testInfo);
         serverPort = serverModule.localAddress().getPort();
     }
@@ -131,6 +141,128 @@ public class ItClientHandlerTest {
         }
     }
 
+    @Test
+    void testHandshakeWithAuthenticationValidCredentials() throws Exception {
+        setupAuthentication("admin", "password");
+
+        try (var sock = new Socket("127.0.0.1", serverPort)) {
+            OutputStream out = sock.getOutputStream();
+
+            // Magic: IGNI
+            out.write(MAGIC);
+
+            // Handshake.
+            var packer = MessagePack.newDefaultBufferPacker();
+            packer.packInt(0);
+            packer.packInt(0);
+            packer.packInt(0);
+            packer.packInt(7 + 8 + 5 + 10 + 10); // Size.
+
+            packer.packInt(3); // Major.
+            packer.packInt(0); // Minor.
+            packer.packInt(0); // Patch.
+
+            packer.packInt(2); // Client type: general purpose.
+
+            packer.packBinaryHeader(0); // Features.
+            packer.packMapHeader(2); // Extensions.
+            packer.packString("username");
+            packer.packString("admin");
+            packer.packString("password");
+            packer.packString("password");
+
+            out.write(packer.toByteArray());
+            out.flush();
+
+            // Read response.
+            var unpacker = 
MessagePack.newDefaultUnpacker(sock.getInputStream());
+            final var magic = unpacker.readPayload(4);
+            unpacker.skipValue(3); // LE int zeros.
+            final var len = unpacker.unpackInt();
+            final var major = unpacker.unpackInt();
+            final var minor = unpacker.unpackInt();
+            final var patch = unpacker.unpackInt();
+            final var success = unpacker.tryUnpackNil();
+            assertTrue(success);
+
+            final var idleTimeout = unpacker.unpackLong();
+            final var nodeId = unpacker.unpackString();
+            final var nodeName = unpacker.unpackString();
+            unpacker.skipValue(); // Cluster id.
+
+            var featuresLen = unpacker.unpackBinaryHeader();
+            unpacker.skipValue(featuresLen);
+
+            var extensionsLen = unpacker.unpackMapHeader();
+            unpacker.skipValue(extensionsLen);
+
+            assertArrayEquals(MAGIC, magic);
+            assertEquals(46, len);
+            assertEquals(3, major);
+            assertEquals(0, minor);
+            assertEquals(0, patch);
+            assertEquals(5000, idleTimeout);
+            assertEquals("id", nodeId);
+            assertEquals("consistent-id", nodeName);
+        }
+    }
+
+    @Test
+    void testHandshakeWithAuthenticationEmptyCredentials() throws Exception {
+        setupAuthentication("admin", "password");
+
+        try (var sock = new Socket("127.0.0.1", serverPort)) {
+            OutputStream out = sock.getOutputStream();
+
+            // Magic: IGNI
+            out.write(MAGIC);
+
+            // Handshake.
+            var packer = MessagePack.newDefaultBufferPacker();
+            packer.packInt(0);
+            packer.packInt(0);
+            packer.packInt(0);
+            packer.packInt(7); // Size.
+
+            packer.packInt(3); // Major.
+            packer.packInt(0); // Minor.
+            packer.packInt(0); // Patch.
+
+            packer.packInt(2); // Client type: general purpose.
+
+            packer.packBinaryHeader(0); // Features.
+            packer.packMapHeader(0); // Extensions.
+
+            out.write(packer.toByteArray());
+            out.flush();
+
+            // Read response.
+            var unpacker = 
MessagePack.newDefaultUnpacker(sock.getInputStream());
+            var magic = unpacker.readPayload(4);
+            unpacker.readPayload(4); // Length.
+            final var major = unpacker.unpackInt();
+            final var minor = unpacker.unpackInt();
+            final var patch = unpacker.unpackInt();
+
+            unpacker.skipValue(); // traceId
+            final var code = unpacker.tryUnpackNil() ? UNKNOWN_ERR : 
unpacker.unpackInt();
+            final var errClassName = unpacker.unpackString();
+            final var errMsg = unpacker.tryUnpackNil() ? null : 
unpacker.unpackString();
+            final var errStackTrace = unpacker.tryUnpackNil() ? null : 
unpacker.unpackString();
+
+            assertArrayEquals(MAGIC, magic);
+            assertEquals(3, major);
+            assertEquals(0, minor);
+            assertEquals(0, patch);
+            assertEquals(COMMON_AUTHENTICATION_ERR, code);
+
+            assertThat(errMsg, containsString("Authentication failed"));
+            
assertEquals("org.apache.ignite.internal.security.exception.AuthenticationException",
 errClassName);
+            assertNull(errStackTrace);
+        }
+    }
+
+
     @Test
     void testHandshakeInvalidVersionReturnsError() throws Exception {
         try (var sock = new Socket("127.0.0.1", serverPort)) {
@@ -193,4 +325,16 @@ public class ItClientHandlerTest {
             out.flush();
         }
     }
+
+    private void setupAuthentication(String username, String password) {
+        authenticationConfiguration.change(change -> {
+            change.changeEnabled(true);
+            change.changeProviders().create("basic", 
authenticationProviderChange -> {
+                
authenticationProviderChange.convert(BasicAuthenticationProviderChange.class)
+                        .changeUsername(username)
+                        .changePassword(password)
+                        .changeName("basic");
+            });
+        }).join();
+    }
 }
diff --git 
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItSslClientHandlerTest.java
 
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItSslClientHandlerTest.java
index 3ace012cb5..928247887b 100644
--- 
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItSslClientHandlerTest.java
+++ 
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItSslClientHandlerTest.java
@@ -77,7 +77,8 @@ public class ItSslClientHandlerTest {
                 TestSslConfig.builder()
                         .keyStorePath(keyStorePkcs12Path)
                         .keyStorePassword("changeit")
-                        .build()
+                        .build(),
+                null
         );
         serverModule = testServer.start(testInfo);
 
@@ -93,7 +94,8 @@ public class ItSslClientHandlerTest {
                 TestSslConfig.builder()
                         .keyStorePath(keyStorePkcs12Path)
                         .keyStorePassword("wrong-password")
-                        .build()
+                        .build(),
+                null
         );
 
         assertThrowsWithCause(() -> testServer.start(testInfo), 
IgniteException.class, "keystore password was incorrect");
diff --git 
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/TestServer.java
 
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/TestServer.java
index 99ec058c12..f2c38c8af1 100644
--- 
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/TestServer.java
+++ 
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/TestServer.java
@@ -28,10 +28,13 @@ import java.util.concurrent.CompletableFuture;
 import javax.annotation.Nullable;
 import 
org.apache.ignite.client.handler.configuration.ClientConnectorConfiguration;
 import org.apache.ignite.compute.IgniteCompute;
+import org.apache.ignite.internal.configuration.AuthenticationConfiguration;
 import org.apache.ignite.internal.configuration.ConfigurationManager;
 import 
org.apache.ignite.internal.configuration.storage.TestConfigurationStorage;
 import org.apache.ignite.internal.metrics.MetricManager;
 import org.apache.ignite.internal.network.configuration.NetworkConfiguration;
+import 
org.apache.ignite.internal.security.authentication.AuthenticationManager;
+import 
org.apache.ignite.internal.security.authentication.AuthenticationManagerImpl;
 import org.apache.ignite.internal.sql.engine.QueryProcessor;
 import org.apache.ignite.internal.table.IgniteTablesInternal;
 import org.apache.ignite.network.ClusterService;
@@ -49,12 +52,21 @@ public class TestServer {
 
     private final TestSslConfig testSslConfig;
 
+    private final AuthenticationConfiguration authenticationConfiguration;
+
     private final ClientHandlerMetricSource metrics = new 
ClientHandlerMetricSource();
 
     private long idleTimeout = 5000;
 
-    TestServer(@Nullable TestSslConfig testSslConfig) {
+    public TestServer() {
+        this(null, null);
+    }
+
+    TestServer(@Nullable TestSslConfig testSslConfig, @Nullable 
AuthenticationConfiguration authenticationConfiguration) {
         this.testSslConfig = testSslConfig;
+        this.authenticationConfiguration = authenticationConfiguration == null
+                ? mock(AuthenticationConfiguration.class)
+                : authenticationConfiguration;
         this.configurationManager = new ConfigurationManager(
                 List.of(ClientConnectorConfiguration.KEY, 
NetworkConfiguration.KEY),
                 Set.of(),
@@ -66,10 +78,6 @@ public class TestServer {
         metrics.enable();
     }
 
-    TestServer() {
-        this(null);
-    }
-
     void idleTimeout(long idleTimeout) {
         this.idleTimeout = idleTimeout;
     }
@@ -106,7 +114,9 @@ public class TestServer {
 
         var module = new ClientHandlerModule(mock(QueryProcessor.class), 
mock(IgniteTablesInternal.class), mock(IgniteTransactions.class),
                 registry, mock(IgniteCompute.class), clusterService, 
bootstrapFactory, mock(IgniteSql.class),
-                () -> CompletableFuture.completedFuture(UUID.randomUUID()), 
mock(MetricManager.class), metrics);
+                () -> CompletableFuture.completedFuture(UUID.randomUUID()), 
mock(MetricManager.class), metrics,
+                authenticationManager(), authenticationConfiguration
+        );
 
         module.start();
 
@@ -121,4 +131,10 @@ public class TestServer {
         var registry = configurationManager.configurationRegistry();
         return registry.getConfiguration(ClientConnectorConfiguration.KEY);
     }
+
+    private AuthenticationManager authenticationManager() {
+        AuthenticationManagerImpl authenticationManager = new 
AuthenticationManagerImpl();
+        authenticationConfiguration.listen(authenticationManager);
+        return authenticationManager;
+    }
 }
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientContext.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientContext.java
index 653aa167d3..8b2964604d 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientContext.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientContext.java
@@ -19,6 +19,7 @@ package org.apache.ignite.client.handler;
 
 import java.util.BitSet;
 import org.apache.ignite.internal.client.proto.ProtocolVersion;
+import org.apache.ignite.internal.security.authentication.UserDetails;
 import org.apache.ignite.internal.tostring.S;
 
 /**
@@ -34,17 +35,21 @@ class ClientContext {
     /** Feature set. */
     private final BitSet features;
 
+    private final UserDetails userDetails;
+
     /**
      * Constructor.
      *
-     * @param version    Version.
+     * @param version Version.
      * @param clientCode Client type code.
-     * @param features   Feature set.
+     * @param features Feature set.
+     * @param userDetails User details.
      */
-    ClientContext(ProtocolVersion version, int clientCode, BitSet features) {
+    ClientContext(ProtocolVersion version, int clientCode, BitSet features, 
UserDetails userDetails) {
         this.version = version;
         this.clientCode = clientCode;
         this.features = features;
+        this.userDetails = userDetails;
     }
 
     /**
@@ -74,6 +79,10 @@ class ClientContext {
         return features;
     }
 
+    public UserDetails userDetails() {
+        return userDetails;
+    }
+
     /** {@inheritDoc} */
     @Override
     public String toString() {
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientHandlerModule.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientHandlerModule.java
index 018ad9c12a..bb91de61d2 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientHandlerModule.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientHandlerModule.java
@@ -34,12 +34,14 @@ import 
org.apache.ignite.client.handler.configuration.ClientConnectorConfigurati
 import org.apache.ignite.client.handler.configuration.ClientConnectorView;
 import org.apache.ignite.compute.IgniteCompute;
 import org.apache.ignite.internal.client.proto.ClientMessageDecoder;
+import org.apache.ignite.internal.configuration.AuthenticationConfiguration;
 import org.apache.ignite.internal.configuration.ConfigurationRegistry;
 import org.apache.ignite.internal.logger.IgniteLogger;
 import org.apache.ignite.internal.logger.Loggers;
 import org.apache.ignite.internal.manager.IgniteComponent;
 import org.apache.ignite.internal.metrics.MetricManager;
 import org.apache.ignite.internal.network.ssl.SslContextProvider;
+import 
org.apache.ignite.internal.security.authentication.AuthenticationManager;
 import org.apache.ignite.internal.sql.engine.QueryProcessor;
 import org.apache.ignite.internal.table.IgniteTablesInternal;
 import org.apache.ignite.lang.IgniteException;
@@ -95,6 +97,10 @@ public class ClientHandlerModule implements IgniteComponent {
     /** Netty bootstrap factory. */
     private final NettyBootstrapFactory bootstrapFactory;
 
+    private final AuthenticationManager authenticationManager;
+
+    private final AuthenticationConfiguration authenticationConfiguration;
+
     /**
      * Constructor.
      *
@@ -108,6 +114,8 @@ public class ClientHandlerModule implements IgniteComponent 
{
      * @param sql SQL.
      * @param clusterIdSupplier ClusterId supplier.
      * @param metricManager Metric manager.
+     * @param authenticationManager Authentication manager.
+     * @param authenticationConfiguration Authentication configuration.
      */
     public ClientHandlerModule(
             QueryProcessor queryProcessor,
@@ -120,7 +128,9 @@ public class ClientHandlerModule implements IgniteComponent 
{
             IgniteSql sql,
             Supplier<CompletableFuture<UUID>> clusterIdSupplier,
             MetricManager metricManager,
-            ClientHandlerMetricSource metrics) {
+            ClientHandlerMetricSource metrics,
+            AuthenticationManager authenticationManager,
+            AuthenticationConfiguration authenticationConfiguration) {
         assert igniteTables != null;
         assert registry != null;
         assert queryProcessor != null;
@@ -129,6 +139,10 @@ public class ClientHandlerModule implements 
IgniteComponent {
         assert bootstrapFactory != null;
         assert sql != null;
         assert clusterIdSupplier != null;
+        assert metricManager != null;
+        assert metrics != null;
+        assert authenticationManager != null;
+        assert authenticationConfiguration != null;
 
         this.queryProcessor = queryProcessor;
         this.igniteTables = igniteTables;
@@ -141,6 +155,8 @@ public class ClientHandlerModule implements IgniteComponent 
{
         this.clusterIdSupplier = clusterIdSupplier;
         this.metricManager = metricManager;
         this.metrics = metrics;
+        this.authenticationManager = authenticationManager;
+        this.authenticationConfiguration = authenticationConfiguration;
     }
 
     /** {@inheritDoc} */
@@ -233,16 +249,7 @@ public class ClientHandlerModule implements 
IgniteComponent {
 
                         ch.pipeline().addLast(
                                 new ClientMessageDecoder(),
-                                new ClientInboundMessageHandler(
-                                        igniteTables,
-                                        igniteTransactions,
-                                        queryProcessor,
-                                        configuration,
-                                        igniteCompute,
-                                        clusterService,
-                                        sql,
-                                        clusterId,
-                                        metrics));
+                                createInboundMessageHandler(configuration));
 
                         metrics.connectionsInitiatedIncrement();
                     }
@@ -277,4 +284,21 @@ public class ClientHandlerModule implements 
IgniteComponent {
 
         return ch.closeFuture();
     }
+
+    private ClientInboundMessageHandler 
createInboundMessageHandler(ClientConnectorView configuration) {
+        ClientInboundMessageHandler clientInboundMessageHandler = new 
ClientInboundMessageHandler(
+                igniteTables,
+                igniteTransactions,
+                queryProcessor,
+                configuration,
+                igniteCompute,
+                clusterService,
+                sql,
+                clusterId,
+                metrics,
+                authenticationManager);
+        authenticationConfiguration.listen(clientInboundMessageHandler);
+        return clientInboundMessageHandler;
+    }
+
 }
diff --git 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
index 76c59e5178..9229755c3b 100644
--- 
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
+++ 
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
@@ -29,6 +29,8 @@ import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
 import io.netty.handler.codec.DecoderException;
 import java.util.BitSet;
+import java.util.EnumMap;
+import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -77,17 +79,24 @@ import 
org.apache.ignite.client.handler.requests.tx.ClientTransactionBeginReques
 import 
org.apache.ignite.client.handler.requests.tx.ClientTransactionCommitRequest;
 import 
org.apache.ignite.client.handler.requests.tx.ClientTransactionRollbackRequest;
 import org.apache.ignite.compute.IgniteCompute;
+import org.apache.ignite.configuration.notifications.ConfigurationListener;
+import 
org.apache.ignite.configuration.notifications.ConfigurationNotificationEvent;
 import org.apache.ignite.internal.client.proto.ClientMessageCommon;
 import org.apache.ignite.internal.client.proto.ClientMessagePacker;
 import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
 import org.apache.ignite.internal.client.proto.ClientOp;
+import org.apache.ignite.internal.client.proto.HandshakeExtension;
 import org.apache.ignite.internal.client.proto.ProtocolVersion;
 import org.apache.ignite.internal.client.proto.ResponseFlags;
 import org.apache.ignite.internal.client.proto.ServerMessageType;
+import org.apache.ignite.internal.configuration.AuthenticationView;
 import org.apache.ignite.internal.jdbc.proto.JdbcQueryCursorHandler;
 import org.apache.ignite.internal.jdbc.proto.JdbcQueryEventHandler;
 import org.apache.ignite.internal.logger.IgniteLogger;
 import org.apache.ignite.internal.logger.Loggers;
+import 
org.apache.ignite.internal.security.authentication.AuthenticationManager;
+import 
org.apache.ignite.internal.security.authentication.AuthenticationRequest;
+import 
org.apache.ignite.internal.security.authentication.UsernamePasswordRequest;
 import org.apache.ignite.internal.sql.engine.QueryProcessor;
 import org.apache.ignite.internal.table.IgniteTablesInternal;
 import org.apache.ignite.internal.util.ExceptionUtils;
@@ -103,7 +112,7 @@ import org.jetbrains.annotations.Nullable;
  * Handles messages from thin clients.
  */
 @SuppressWarnings({"rawtypes", "unchecked"})
-public class ClientInboundMessageHandler extends ChannelInboundHandlerAdapter {
+public class ClientInboundMessageHandler extends ChannelInboundHandlerAdapter 
implements ConfigurationListener<AuthenticationView> {
     /** The logger. */
     private static final IgniteLogger LOG = 
Loggers.forClass(ClientInboundMessageHandler.class);
 
@@ -143,12 +152,18 @@ public class ClientInboundMessageHandler extends 
ChannelInboundHandlerAdapter {
     /** Context. */
     private ClientContext clientContext;
 
+    /** Chanel handler context. */
+    private ChannelHandlerContext channelHandlerContext;
+
     /** Whether the partition assignment has changed since the last server 
response. */
     private final AtomicBoolean partitionAssignmentChanged = new 
AtomicBoolean();
 
     /** Partition assignment change listener. */
     private final Consumer<IgniteTablesInternal> 
partitionAssignmentsChangeListener;
 
+    /** Authentication manager. */
+    private final AuthenticationManager authenticationManager;
+
     /**
      * Constructor.
      *
@@ -161,6 +176,7 @@ public class ClientInboundMessageHandler extends 
ChannelInboundHandlerAdapter {
      * @param sql SQL.
      * @param clusterId Cluster ID.
      * @param metrics Metrics.
+     * @param authenticationManager Authentication manager.
      */
     public ClientInboundMessageHandler(
             IgniteTablesInternal igniteTables,
@@ -171,7 +187,9 @@ public class ClientInboundMessageHandler extends 
ChannelInboundHandlerAdapter {
             ClusterService clusterService,
             IgniteSql sql,
             UUID clusterId,
-            ClientHandlerMetricSource metrics) {
+            ClientHandlerMetricSource metrics,
+            AuthenticationManager authenticationManager
+    ) {
         assert igniteTables != null;
         assert igniteTransactions != null;
         assert processor != null;
@@ -181,6 +199,7 @@ public class ClientInboundMessageHandler extends 
ChannelInboundHandlerAdapter {
         assert sql != null;
         assert clusterId != null;
         assert metrics != null;
+        assert authenticationManager != null;
 
         this.igniteTables = igniteTables;
         this.igniteTransactions = igniteTransactions;
@@ -190,6 +209,7 @@ public class ClientInboundMessageHandler extends 
ChannelInboundHandlerAdapter {
         this.sql = sql;
         this.clusterId = clusterId;
         this.metrics = metrics;
+        this.authenticationManager = authenticationManager;
 
         jdbcQueryEventHandler = new JdbcQueryEventHandlerImpl(processor, new 
JdbcMetadataCatalog(igniteTables), resources);
         jdbcQueryCursorHandler = new JdbcQueryCursorHandlerImpl(resources);
@@ -198,6 +218,12 @@ public class ClientInboundMessageHandler extends 
ChannelInboundHandlerAdapter {
         
igniteTables.addAssignmentsChangeListener(partitionAssignmentsChangeListener);
     }
 
+    @Override
+    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
+        channelHandlerContext = ctx;
+        super.channelRegistered(ctx);
+    }
+
     /** {@inheritDoc} */
     @Override
     public void channelRead(ChannelHandlerContext ctx, Object msg) {
@@ -241,16 +267,16 @@ public class ClientInboundMessageHandler extends 
ChannelInboundHandlerAdapter {
             var clientCode = unpacker.unpackInt();
             var featuresLen = unpacker.unpackBinaryHeader();
             var features = BitSet.valueOf(unpacker.readPayload(featuresLen));
+            var extensions = extractExtensions(unpacker);
+
+            var userDetails = 
authenticationManager.authenticate(createAuthenticationRequest(extensions));
 
-            clientContext = new ClientContext(clientVer, clientCode, features);
+            clientContext = new ClientContext(clientVer, clientCode, features, 
userDetails);
 
             if (LOG.isDebugEnabled()) {
                 LOG.debug("Handshake [remoteAddress=" + 
ctx.channel().remoteAddress() + "]: " + clientContext);
             }
 
-            var extensionsLen = unpacker.unpackMapHeader();
-            unpacker.skipValues(extensionsLen);
-
             // Response.
             ProtocolVersion.LATEST_VER.pack(packer);
             packer.packNil(); // No error.
@@ -295,6 +321,11 @@ public class ClientInboundMessageHandler extends 
ChannelInboundHandlerAdapter {
         }
     }
 
+    private AuthenticationRequest<?, ?> 
createAuthenticationRequest(Map<HandshakeExtension, Object> extensions) {
+        return new UsernamePasswordRequest((String) 
extensions.get(HandshakeExtension.USERNAME),
+                (String) extensions.get(HandshakeExtension.PASSWORD));
+    }
+
     private void writeMagic(ChannelHandlerContext ctx) {
         ctx.write(Unpooled.wrappedBuffer(ClientMessageCommon.MAGIC_BYTES));
         metrics.bytesSentAdd(ClientMessageCommon.MAGIC_BYTES.length);
@@ -608,4 +639,33 @@ public class ClientInboundMessageHandler extends 
ChannelInboundHandlerAdapter {
 
         ctx.close();
     }
+
+    @Override
+    public CompletableFuture<?> 
onUpdate(ConfigurationNotificationEvent<AuthenticationView> ctx) {
+        if (clientContext != null && channelHandlerContext != null) {
+            channelHandlerContext.close();
+        }
+        return CompletableFuture.completedFuture(null);
+    }
+
+    private Map<HandshakeExtension, Object> 
extractExtensions(ClientMessageUnpacker unpacker) {
+        EnumMap<HandshakeExtension, Object> extensions = new 
EnumMap<>(HandshakeExtension.class);
+        int mapSize = unpacker.unpackMapHeader();
+        for (int i = 0; i < mapSize; i++) {
+            HandshakeExtension handshakeExtension = 
HandshakeExtension.fromKey(unpacker.unpackString());
+            if (handshakeExtension != null) {
+                extensions.put(handshakeExtension, 
unpackExtensionValue(handshakeExtension, unpacker));
+            }
+        }
+        return extensions;
+    }
+
+    private Object unpackExtensionValue(HandshakeExtension handshakeExtension, 
ClientMessageUnpacker unpacker) {
+        Class<?> type = handshakeExtension.valueType();
+        if (type == String.class) {
+            return unpacker.unpackString();
+        } else {
+            throw new IllegalArgumentException("Unsupported extension type: " 
+ type.getName());
+        }
+    }
 }
diff --git a/modules/client/build.gradle b/modules/client/build.gradle
index 1d87974f4e..8cab8c304a 100644
--- a/modules/client/build.gradle
+++ b/modules/client/build.gradle
@@ -49,6 +49,7 @@ dependencies {
     testImplementation project(':ignite-raft-api')
     testImplementation project(':ignite-transactions')
     testImplementation project(':ignite-storage-api')
+    testImplementation project(':ignite-security')
     testImplementation project(':ignite-metrics')
     testImplementation(testFixtures(project(':ignite-core')))
     testImplementation(testFixtures(project(':ignite-configuration')))
diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTest.java 
b/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTest.java
index c268e449fa..76d2c6ee06 100644
--- 
a/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTest.java
+++ 
b/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTest.java
@@ -141,7 +141,7 @@ public abstract class AbstractClientTest {
             Ignite ignite,
             String nodeName
     ) {
-        return new TestServer(port, portRange, idleTimeout, ignite, null, 
null, nodeName, clusterId);
+        return new TestServer(port, portRange, idleTimeout, ignite, null, 
null, nodeName, clusterId, null);
     }
 
     /**
diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/ClientComputeTest.java 
b/modules/client/src/test/java/org/apache/ignite/client/ClientComputeTest.java
index ebca20cf41..5c74c45e4e 100644
--- 
a/modules/client/src/test/java/org/apache/ignite/client/ClientComputeTest.java
+++ 
b/modules/client/src/test/java/org/apache/ignite/client/ClientComputeTest.java
@@ -163,8 +163,8 @@ public class ClientComputeTest {
 
         var clusterId = UUID.randomUUID();
 
-        server1 = new TestServer(10900, 10, 0, ignite, shouldDropConnection, 
null, "s1", clusterId);
-        server2 = new TestServer(10910, 10, 0, ignite, shouldDropConnection, 
null, "s2", clusterId);
-        server3 = new TestServer(10920, 10, 0, ignite, shouldDropConnection, 
null, "s3", clusterId);
+        server1 = new TestServer(10900, 10, 0, ignite, shouldDropConnection, 
null, "s1", clusterId, null);
+        server2 = new TestServer(10910, 10, 0, ignite, shouldDropConnection, 
null, "s2", clusterId, null);
+        server3 = new TestServer(10920, 10, 0, ignite, shouldDropConnection, 
null, "s3", clusterId, null);
     }
 }
diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/ClientMetricsTest.java 
b/modules/client/src/test/java/org/apache/ignite/client/ClientMetricsTest.java
index 3b2277ea5a..99a40a1ebf 100644
--- 
a/modules/client/src/test/java/org/apache/ignite/client/ClientMetricsTest.java
+++ 
b/modules/client/src/test/java/org/apache/ignite/client/ClientMetricsTest.java
@@ -70,7 +70,17 @@ public class ClientMetricsTest {
     public void testConnectionsLostTimeout() throws InterruptedException {
         Function<Integer, Boolean> shouldDropConnection = requestIdx -> 
requestIdx == 0;
         Function<Integer, Integer> responseDelay = idx -> idx > 1 ? 500 : 0;
-        server = new TestServer(10800, 10, 1000, new FakeIgnite(), 
shouldDropConnection, responseDelay, null, AbstractClientTest.clusterId);
+        server = new TestServer(
+                10800,
+                10,
+                1000,
+                new FakeIgnite(),
+                shouldDropConnection,
+                responseDelay,
+                null,
+                AbstractClientTest.clusterId,
+                null
+        );
         client = clientBuilder()
                 .connectTimeout(100)
                 .heartbeatTimeout(100)
@@ -86,7 +96,17 @@ public class ClientMetricsTest {
     public void testHandshakesFailed() {
         AtomicInteger counter = new AtomicInteger();
         Function<Integer, Boolean> shouldDropConnection = requestIdx -> 
counter.incrementAndGet() < 3; // Fail 2 handshakes.
-        server = new TestServer(10800, 10, 1000, new FakeIgnite(), 
shouldDropConnection, null, null, AbstractClientTest.clusterId);
+        server = new TestServer(
+                10800,
+                10,
+                1000,
+                new FakeIgnite(),
+                shouldDropConnection,
+                null,
+                null,
+                AbstractClientTest.clusterId,
+                null
+        );
 
         client = clientBuilder().build();
 
@@ -98,7 +118,17 @@ public class ClientMetricsTest {
         AtomicInteger counter = new AtomicInteger();
         Function<Integer, Boolean> shouldDropConnection = requestIdx -> false;
         Function<Integer, Integer> responseDelay = idx -> 
counter.incrementAndGet() == 1 ? 500 : 0;
-        server = new TestServer(10800, 10, 1000, new FakeIgnite(), 
shouldDropConnection, responseDelay, null, AbstractClientTest.clusterId);
+        server = new TestServer(
+                10800,
+                10,
+                1000,
+                new FakeIgnite(),
+                shouldDropConnection,
+                responseDelay,
+                null,
+                AbstractClientTest.clusterId,
+                null
+        );
         client = clientBuilder()
                 .connectTimeout(100)
                 .build();
@@ -112,7 +142,17 @@ public class ClientMetricsTest {
     public void testRequestsMetrics() throws InterruptedException {
         Function<Integer, Boolean> shouldDropConnection = requestIdx -> 
requestIdx == 5;
         Function<Integer, Integer> responseDelay = idx -> idx == 4 ? 1000 : 0;
-        server = new TestServer(10800, 10, 1000, new FakeIgnite(), 
shouldDropConnection, responseDelay, null, AbstractClientTest.clusterId);
+        server = new TestServer(
+                10800,
+                10,
+                1000,
+                new FakeIgnite(),
+                shouldDropConnection,
+                responseDelay,
+                null,
+                AbstractClientTest.clusterId,
+                null
+        );
         client = clientBuilder().build();
 
         assertEquals(0, metrics().requestsActive());
diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/ConfigurationTest.java 
b/modules/client/src/test/java/org/apache/ignite/client/ConfigurationTest.java
index ab74671864..4a775f3512 100644
--- 
a/modules/client/src/test/java/org/apache/ignite/client/ConfigurationTest.java
+++ 
b/modules/client/src/test/java/org/apache/ignite/client/ConfigurationTest.java
@@ -161,7 +161,7 @@ public class ConfigurationTest extends AbstractClientTest {
     public void testCustomAsyncContinuationExecutor() throws Exception {
         Function<Integer, Integer> responseDelay = x -> 50;
 
-        try (var testServer = new TestServer(10900, 10, 0, server, x -> false, 
responseDelay, "n2", clusterId)) {
+        try (var testServer = new TestServer(10900, 10, 0, server, x -> false, 
responseDelay, "n2", clusterId, null)) {
             ExecutorService executor = Executors.newSingleThreadExecutor();
 
             var builderThreadName = new AtomicReference<String>();
diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/ConnectionTest.java 
b/modules/client/src/test/java/org/apache/ignite/client/ConnectionTest.java
index f294e9be1b..58840ec84f 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/ConnectionTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/ConnectionTest.java
@@ -81,7 +81,7 @@ public class ConnectionTest extends AbstractClientTest {
     public void testNoResponseFromServerWithinConnectTimeoutThrowsException() 
throws Exception {
         Function<Integer, Integer> responseDelay = x -> 500;
 
-        try (var srv = new TestServer(10800, 10, 300, new FakeIgnite(), x -> 
false, responseDelay, null, UUID.randomUUID())) {
+        try (var srv = new TestServer(10800, 10, 300, new FakeIgnite(), x -> 
false, responseDelay, null, UUID.randomUUID(), null)) {
             Builder builder = IgniteClient.builder()
                     .addresses("127.0.0.1:" + srv.port())
                     .retryPolicy(new RetryLimitPolicy().retryLimit(1))
diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/HeartbeatTest.java 
b/modules/client/src/test/java/org/apache/ignite/client/HeartbeatTest.java
index aeeb47112e..40776b15be 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/HeartbeatTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/HeartbeatTest.java
@@ -87,7 +87,7 @@ public class HeartbeatTest {
         Function<Integer, Integer> responseDelayFunc = requestCount -> 
requestCount > 1 ? 500 : 0;
         var loggerFactory = new TestLoggerFactory("client");
 
-        try (var srv = new TestServer(10800, 10, 300, new FakeIgnite(), x -> 
false, responseDelayFunc, null, UUID.randomUUID())) {
+        try (var srv = new TestServer(10800, 10, 300, new FakeIgnite(), x -> 
false, responseDelayFunc, null, UUID.randomUUID(), null)) {
             int srvPort = srv.port();
 
             Builder builder = IgniteClient.builder()
diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/MultiClusterTest.java 
b/modules/client/src/test/java/org/apache/ignite/client/MultiClusterTest.java
index b94c2ae023..94db576399 100644
--- 
a/modules/client/src/test/java/org/apache/ignite/client/MultiClusterTest.java
+++ 
b/modules/client/src/test/java/org/apache/ignite/client/MultiClusterTest.java
@@ -48,8 +48,8 @@ public class MultiClusterTest {
 
     @BeforeEach
     void setUp() {
-        server1 = new TestServer(10900, 10, 0, new FakeIgnite(), null, null, 
"s1", clusterId1);
-        server2 = new TestServer(10900, 10, 0, new FakeIgnite(), null, null, 
"s2", clusterId2);
+        server1 = new TestServer(10900, 10, 0, new FakeIgnite(), null, null, 
"s1", clusterId1, null);
+        server2 = new TestServer(10900, 10, 0, new FakeIgnite(), null, null, 
"s2", clusterId2, null);
     }
 
     @AfterEach
@@ -90,7 +90,7 @@ public class MultiClusterTest {
             client.tables().tables();
 
             server1.close();
-            server1 = new TestServer(10900, 10, 0, new FakeIgnite(), null, 
null, "s1", clusterId2);
+            server1 = new TestServer(10900, 10, 0, new FakeIgnite(), null, 
null, "s1", clusterId2, null);
 
             IgniteClientConnectionException ex = 
(IgniteClientConnectionException) assertThrowsWithCause(
                     () -> client.tables().tables(), 
IgniteClientConnectionException.class, "Cluster ID mismatch");
diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/RetryPolicyTest.java 
b/modules/client/src/test/java/org/apache/ignite/client/RetryPolicyTest.java
index 133f204bde..07f4de771f 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/RetryPolicyTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/RetryPolicyTest.java
@@ -279,6 +279,6 @@ public class RetryPolicyTest {
         FakeIgnite ign = new FakeIgnite();
         ((FakeIgniteTables) ign.tables()).createTable("t");
 
-        server = new TestServer(10900, 10, 0, ign, shouldDropConnection, null, 
null, UUID.randomUUID());
+        server = new TestServer(10900, 10, 0, ign, shouldDropConnection, null, 
null, UUID.randomUUID(), null);
     }
 }
diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/TestClientHandlerModule.java
 
b/modules/client/src/test/java/org/apache/ignite/client/TestClientHandlerModule.java
index 20dd355b30..8b90e8de12 100644
--- 
a/modules/client/src/test/java/org/apache/ignite/client/TestClientHandlerModule.java
+++ 
b/modules/client/src/test/java/org/apache/ignite/client/TestClientHandlerModule.java
@@ -38,8 +38,11 @@ import 
org.apache.ignite.client.handler.ClientInboundMessageHandler;
 import 
org.apache.ignite.client.handler.configuration.ClientConnectorConfiguration;
 import org.apache.ignite.compute.IgniteCompute;
 import org.apache.ignite.internal.client.proto.ClientMessageDecoder;
+import org.apache.ignite.internal.configuration.AuthenticationConfiguration;
 import org.apache.ignite.internal.configuration.ConfigurationRegistry;
 import org.apache.ignite.internal.manager.IgniteComponent;
+import 
org.apache.ignite.internal.security.authentication.AuthenticationManager;
+import 
org.apache.ignite.internal.security.authentication.AuthenticationManagerImpl;
 import org.apache.ignite.internal.sql.engine.QueryProcessor;
 import org.apache.ignite.internal.table.IgniteTablesInternal;
 import org.apache.ignite.lang.IgniteException;
@@ -82,6 +85,9 @@ public class TestClientHandlerModule implements 
IgniteComponent {
     /** Netty bootstrap factory. */
     private final NettyBootstrapFactory bootstrapFactory;
 
+    /** Authentication configuration. */
+    private final AuthenticationConfiguration authenticationConfiguration;
+
     /**
      * Constructor.
      *
@@ -104,7 +110,8 @@ public class TestClientHandlerModule implements 
IgniteComponent {
             ClusterService clusterService,
             IgniteCompute compute,
             UUID clusterId,
-            ClientHandlerMetricSource metrics) {
+            ClientHandlerMetricSource metrics,
+            AuthenticationConfiguration authenticationConfiguration) {
         assert ignite != null;
         assert registry != null;
         assert bootstrapFactory != null;
@@ -118,6 +125,7 @@ public class TestClientHandlerModule implements 
IgniteComponent {
         this.compute = compute;
         this.clusterId = clusterId;
         this.metrics = metrics;
+        this.authenticationConfiguration = authenticationConfiguration;
     }
 
     /** {@inheritDoc} */
@@ -189,7 +197,8 @@ public class TestClientHandlerModule implements 
IgniteComponent {
                                         clusterService,
                                         mock(IgniteSql.class),
                                         clusterId,
-                                        metrics));
+                                        metrics,
+                                        
authenticationManager(authenticationConfiguration)));
                     }
                 })
                 .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 
configuration.connectTimeout());
@@ -273,4 +282,10 @@ public class TestClientHandlerModule implements 
IgniteComponent {
             super.channelRead(ctx, msg);
         }
     }
+
+    private AuthenticationManager 
authenticationManager(AuthenticationConfiguration authenticationConfiguration) {
+        AuthenticationManagerImpl manager = new AuthenticationManagerImpl();
+        authenticationConfiguration.listen(manager);
+        return manager;
+    }
 }
diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/TestServer.java 
b/modules/client/src/test/java/org/apache/ignite/client/TestServer.java
index 5cacb8b9fa..3c8a03ebee 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/TestServer.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/TestServer.java
@@ -37,11 +37,14 @@ import 
org.apache.ignite.client.handler.ClientHandlerMetricSource;
 import org.apache.ignite.client.handler.ClientHandlerModule;
 import 
org.apache.ignite.client.handler.configuration.ClientConnectorConfiguration;
 import org.apache.ignite.compute.IgniteCompute;
+import org.apache.ignite.internal.configuration.AuthenticationConfiguration;
 import org.apache.ignite.internal.configuration.ConfigurationRegistry;
 import 
org.apache.ignite.internal.configuration.storage.TestConfigurationStorage;
 import org.apache.ignite.internal.manager.IgniteComponent;
 import org.apache.ignite.internal.metrics.MetricManager;
 import org.apache.ignite.internal.network.configuration.NetworkConfiguration;
+import 
org.apache.ignite.internal.security.authentication.AuthenticationManager;
+import 
org.apache.ignite.internal.security.authentication.AuthenticationManagerImpl;
 import org.apache.ignite.internal.table.IgniteTablesInternal;
 import org.apache.ignite.network.ClusterNode;
 import org.apache.ignite.network.ClusterService;
@@ -78,7 +81,17 @@ public class TestServer implements AutoCloseable {
             long idleTimeout,
             Ignite ignite
     ) {
-        this(port, portRange, idleTimeout, ignite, null, null, null, 
UUID.randomUUID());
+        this(
+                port,
+                portRange,
+                idleTimeout,
+                ignite,
+                null,
+                null,
+                null,
+                UUID.randomUUID(),
+                null
+        );
     }
 
     /**
@@ -97,7 +110,8 @@ public class TestServer implements AutoCloseable {
             @Nullable Function<Integer, Boolean> shouldDropConnection,
             @Nullable Function<Integer, Integer> responseDelay,
             @Nullable String nodeName,
-            UUID clusterId
+            UUID clusterId,
+            @Nullable AuthenticationConfiguration authenticationConfiguration
     ) {
         cfg = new ConfigurationRegistry(
                 List.of(ClientConnectorConfiguration.KEY, 
NetworkConfiguration.KEY),
@@ -135,6 +149,9 @@ public class TestServer implements AutoCloseable {
         metrics = new ClientHandlerMetricSource();
         metrics.enable();
 
+        AuthenticationConfiguration authenticationConfigToApply = 
authenticationConfiguration == null
+                ? mock(AuthenticationConfiguration.class)
+                : authenticationConfiguration;
         module = shouldDropConnection != null
                 ? new TestClientHandlerModule(
                         ignite,
@@ -145,7 +162,8 @@ public class TestServer implements AutoCloseable {
                         clusterService,
                         compute,
                         clusterId,
-                        metrics)
+                        metrics,
+                        authenticationConfigToApply)
                 : new ClientHandlerModule(
                         ((FakeIgnite) ignite).queryEngine(),
                         (IgniteTablesInternal) ignite.tables(),
@@ -157,7 +175,10 @@ public class TestServer implements AutoCloseable {
                         ignite.sql(),
                         () -> CompletableFuture.completedFuture(clusterId),
                         mock(MetricManager.class),
-                        metrics);
+                        metrics,
+                        authenticationManager(authenticationConfigToApply),
+                        authenticationConfigToApply
+                        );
 
         module.start();
     }
@@ -217,4 +238,10 @@ public class TestServer implements AutoCloseable {
     private static String getNodeId(String name) {
         return name + "-id";
     }
+
+    private AuthenticationManager 
authenticationManager(AuthenticationConfiguration authenticationConfiguration) {
+        AuthenticationManagerImpl authenticationManager = new 
AuthenticationManagerImpl();
+        authenticationConfiguration.listen(authenticationManager);
+        return authenticationManager;
+    }
 }
diff --git 
a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java 
b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
index 2a830facab..22ffb1eabd 100644
--- 
a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
+++ 
b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
@@ -514,6 +514,11 @@ public class IgniteImpl implements Ignite {
 
         compute = new IgniteComputeImpl(clusterSvc.topologyService(), 
distributedTblMgr, computeComponent);
 
+        authenticationManager = createAuthenticationManager();
+
+        AuthenticationConfiguration authenticationConfiguration = 
clusterConfigRegistry.getConfiguration(SecurityConfiguration.KEY)
+                .authentication();
+
         clientHandlerModule = new ClientHandlerModule(
                 qryEngine,
                 distributedTblMgr,
@@ -525,8 +530,10 @@ public class IgniteImpl implements Ignite {
                 sql,
                 () -> cmgMgr.clusterState().thenApply(s -> 
s.clusterTag().clusterId()),
                 metricManager,
-                new ClientHandlerMetricSource()
-        );
+                new ClientHandlerMetricSource(),
+                authenticationManager,
+                authenticationConfiguration
+                );
 
         deploymentManager = new DeploymentManagerImpl(clusterSvc,
                 metaStorageMgr,
@@ -534,7 +541,6 @@ public class IgniteImpl implements Ignite {
                 
nodeConfigRegistry.getConfiguration(DeploymentConfiguration.KEY),
                 cmgMgr);
 
-        authenticationManager = createAuthenticationManager();
         restComponent = createRestComponent(name);
     }
 

Reply via email to