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); }