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

apkhmv 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 eccada42fa IGNITE-20705 Switch user context when reconnecting (#2762)
eccada42fa is described below

commit eccada42faded5c109b95f4d7c3454b1c9e0642d
Author: Vadim Pakhnushev <[email protected]>
AuthorDate: Wed Nov 1 17:16:20 2023 +0300

    IGNITE-20705 Switch user context when reconnecting (#2762)
---
 ...liCommandTestNotInitializedIntegrationBase.java |   5 +-
 ...tConnectWithBasicAuthenticationCommandTest.java | 144 ++++++++++++++++++++-
 .../questions/ItConnectToBasicAuthClusterTest.java |   4 +-
 .../ItConnectToSslAndAuthClusterTest.java          |  19 +--
 .../internal/cli/call/connect/ConnectCall.java     |  29 ++++-
 .../cli/call/connect/ConnectCallInput.java         |   6 +-
 .../cli/call/connect/ConnectWizardCall.java        |  24 +++-
 .../cli/call/connect/ConnectionChecker.java        |   5 -
 .../cli/commands/connect/ConnectOptions.java       |   8 +-
 .../cli/commands/connect/ConnectReplCommand.java   |  28 ++--
 .../internal/cli/commands/node/NodeNameOrUrl.java  |  33 -----
 .../questions/ConnectToClusterQuestion.java        |  59 ++++++---
 .../core/converters/NodeNameOrUrlConverter.java    |  61 ---------
 .../ignite/internal/cli/core/repl/Session.java     |  10 +-
 .../completer/DynamicCompleterActivationPoint.java |   6 -
 .../repl/completer/DynamicCompletionInsider.java   |   2 -
 .../cli/core/repl/executor/ReplExecutorImpl.java   |  10 +-
 .../repl/executor/ReplExecutorProviderImpl.java    |   6 +-
 .../internal/cli/core/style/AnsiStringSupport.java |   2 +
 .../cli/core/style/element/UiElements.java         |   5 +
 .../internal/cli/core/style/element/UiString.java  |   2 +-
 .../internal/cli/commands/CliCommandTestBase.java  |   5 +-
 .../cli/commands/UrlOptionsNegativeTest.java       |   7 +-
 .../cli/commands/connect/ConnectCommandTest.java   |   5 +-
 .../completer/DynamicCompleterRegistryTest.java    |  10 +-
 .../completer/DynamicCompletionInsiderTest.java    |   6 -
 .../cluster-configuration-with-enabled-auth.conf   |   6 +
 27 files changed, 284 insertions(+), 223 deletions(-)

diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
index b0468651d8..cf350a9caf 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
@@ -27,9 +27,7 @@ import java.io.StringWriter;
 import org.apache.ignite.internal.cli.CliIntegrationTestBase;
 import 
org.apache.ignite.internal.cli.commands.cliconfig.TestConfigManagerHelper;
 import 
org.apache.ignite.internal.cli.commands.cliconfig.TestConfigManagerProvider;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
 import org.apache.ignite.internal.cli.config.ConfigDefaultValueProvider;
-import org.apache.ignite.internal.cli.core.converters.NodeNameOrUrlConverter;
 import org.apache.ignite.internal.cli.core.repl.EventListeningActivationPoint;
 import 
org.apache.ignite.internal.cli.core.repl.context.CommandLineContextProvider;
 import org.apache.ignite.internal.cli.core.repl.registry.JdbcUrlRegistry;
@@ -83,8 +81,7 @@ public class CliCommandTestNotInitializedIntegrationBase 
extends CliIntegrationT
     @BeforeEach
     public void setUp() {
         
configManagerProvider.setConfigFile(TestConfigManagerHelper.createIntegrationTestsConfig());
-        cmd = new CommandLine(getCommandClass(), new MicronautFactory(context))
-                .registerConverter(NodeNameOrUrl.class, new 
NodeNameOrUrlConverter(nodeNameRegistry));
+        cmd = new CommandLine(getCommandClass(), new 
MicronautFactory(context));
         cmd.setDefaultValueProvider(configDefaultValueProvider);
         eventListeningActivationPoint.subscribe();
         resetOutput();
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectWithBasicAuthenticationCommandTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectWithBasicAuthenticationCommandTest.java
index 89f8f8c28b..19c32ed0f9 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectWithBasicAuthenticationCommandTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/connect/ItConnectWithBasicAuthenticationCommandTest.java
@@ -246,8 +246,7 @@ class ItConnectWithBasicAuthenticationCommandTest extends 
ItConnectToClusterTest
         // Given basic authentication is NOT configured in config file
         configManagerProvider.setConfigFile(createIntegrationTestsConfig());
         // Given prompt before connect
-        String promptBefore = getPrompt();
-        assertThat(promptBefore).isEqualTo("[disconnected]> ");
+        assertThat(getPrompt()).isEqualTo("[disconnected]> ");
 
         // And answer is "y"
         bindAnswers("y");
@@ -292,4 +291,145 @@ class ItConnectWithBasicAuthenticationCommandTest extends 
ItConnectToClusterTest
                         
configManagerProvider.get().getCurrentProperty(Constants.BASIC_AUTHENTICATION_PASSWORD))
         );
     }
+
+    @Test
+    void reconnectWithDifferentAuthenticationParameters() throws IOException {
+        // Given basic authentication is configured in config file
+        configManagerProvider.setConfigFile(createIntegrationTestsConfig(), 
createJdbcTestsBasicSecretConfig());
+
+        // Given prompt before connect
+        assertThat(getPrompt()).isEqualTo("[disconnected]> ");
+
+        // When connect with auth parameters
+        execute("connect", "--username", "admin", "--password", "password");
+
+        // Then
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Connected to 
http://localhost:10300";)
+        );
+
+        // And prompt shows username and node name
+        assertThat(getPrompt()).isEqualTo("[admin:" + nodeName() + "]> ");
+
+        resetOutput();
+
+        // Should ask user to reconnect with different user, answer "y"
+        bindAnswers("y");
+
+        // When connect with different auth parameters
+        execute("connect", "--username", "admin1", "--password", "password");
+
+        // Then
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Connected to 
http://localhost:10300";)
+        );
+
+        // And prompt shows username and node name
+        assertThat(getPrompt()).isEqualTo("[admin1:" + nodeName() + "]> ");
+    }
+
+    @Test
+    void reconnectWithAuthenticationParametersOverridingConfig() throws 
IOException {
+        // Given basic authentication is configured in config file
+        configManagerProvider.setConfigFile(createIntegrationTestsConfig(), 
createJdbcTestsBasicSecretConfig());
+
+        // Given prompt before connect
+        assertThat(getPrompt()).isEqualTo("[disconnected]> ");
+
+        // When connect without auth parameters
+        execute("connect");
+
+        // Then
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Connected to 
http://localhost:10300";)
+        );
+
+        // And prompt shows username and node name
+        assertThat(getPrompt()).isEqualTo("[admin:" + nodeName() + "]> ");
+
+        resetOutput();
+
+        // Should ask user to reconnect with different user, answer "y"
+        bindAnswers("y");
+
+        // When connect with different auth parameters
+        execute("connect", "--username", "admin1", "--password", "password");
+
+        // Then
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Connected to 
http://localhost:10300";)
+        );
+
+        // And prompt shows username and node name
+        assertThat(getPrompt()).isEqualTo("[admin1:" + nodeName() + "]> ");
+    }
+
+    @Test
+    void reconnectWithAuthenticationParametersFromConfig() throws IOException {
+        // Given basic authentication is configured in config file
+        configManagerProvider.setConfigFile(createIntegrationTestsConfig(), 
createJdbcTestsBasicSecretConfig());
+
+        // Given prompt before connect
+        assertThat(getPrompt()).isEqualTo("[disconnected]> ");
+
+        // When connect with auth parameters overriding config
+        execute("connect", "--username", "admin1", "--password", "password");
+
+        // Then
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Connected to 
http://localhost:10300";)
+        );
+
+        // And prompt shows username from parameters and node name
+        assertThat(getPrompt()).isEqualTo("[admin1:" + nodeName() + "]> ");
+
+        resetOutput();
+
+        // Should ask user to reconnect with different user, answer "y"
+        bindAnswers("y");
+
+        // When connect without auth parameters
+        execute("connect");
+
+        // Then
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Connected to 
http://localhost:10300";)
+        );
+
+        // And prompt shows username from config and node name
+        assertThat(getPrompt()).isEqualTo("[admin:" + nodeName() + "]> ");
+    }
+
+    @Test
+    void shouldStoreCredentialsFromAnswer() throws IOException {
+        // Given basic authentication is NOT configured in config file
+        configManagerProvider.setConfigFile(createIntegrationTestsConfig());
+        // Given prompt before connect
+        assertThat(getPrompt()).isEqualTo("[disconnected]> ");
+
+        // And answer "y" to question on auth error, enter username and 
password and answer "y" to save config question
+        bindAnswers("y", "admin", "password", "y");
+
+        // And connected
+        execute("connect", "--username", "admin", "--password", 
"wrong-password");
+
+        // And output is
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputIs(
+                        "Config saved" + System.lineSeparator() + "Connected 
to http://localhost:10300"; + System.lineSeparator())
+        );
+
+        // And prompt shows username and node name
+        assertThat(getPrompt()).isEqualTo("[admin:" + nodeName() + "]> ");
+        // And correct values are stored in config
+        assertEquals("admin", 
configManagerProvider.get().getCurrentProperty(Constants.BASIC_AUTHENTICATION_USERNAME));
+        assertEquals("password", 
configManagerProvider.get().getCurrentProperty(Constants.BASIC_AUTHENTICATION_PASSWORD));
+    }
 }
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToBasicAuthClusterTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToBasicAuthClusterTest.java
index 2fe83492bb..68eeb56ed1 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToBasicAuthClusterTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToBasicAuthClusterTest.java
@@ -42,8 +42,8 @@ class ItConnectToBasicAuthClusterTest extends 
ItConnectToClusterTestBase {
         assertThat(getPrompt()).isEqualTo("[disconnected]> ");
 
         // And answer to the reconnect question is "y", to the auth 
configuration question is "y",
-        // username and password are provided
-        bindAnswers("y", "y", "admin", "password");
+        // username and password are provided and answer to save 
authentication is "y"
+        bindAnswers("y", "y", "admin", "password", "y");
 
         // When asked the question
         question.askQuestionOnReplStart();
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToSslAndAuthClusterTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToSslAndAuthClusterTest.java
index 90a3255b3c..1b785e6612 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToSslAndAuthClusterTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/questions/ItConnectToSslAndAuthClusterTest.java
@@ -59,9 +59,10 @@ class ItConnectToSslAndAuthClusterTest extends 
ItConnectToClusterTestBase {
         stateConfigProvider.config = 
TestStateConfigHelper.createLastConnectedSslDefault();
 
         // And answer to the reconnect question is "y", to the SSL 
configuration question is "y",
-        // trust store path and password are provided and key store is not 
configured
+        // trust store path and password are provided and answer to key store 
configuration is "n",
+        // answer to auth configuration is "y", username and password is 
provided and answer to save authentication is "y"
         //ToDo: check question as well IGNITE-20324
-        bindAnswers("y", "y", NodeConfig.resolvedTruststorePath, 
NodeConfig.trustStorePassword, "n", "y", "admin", "password");
+        bindAnswers("y", "y", NodeConfig.resolvedTruststorePath, 
NodeConfig.trustStorePassword, "n", "y", "admin", "password", "y");
 
         // When asked the question
         question.askQuestionOnReplStart();
@@ -257,9 +258,10 @@ class ItConnectToSslAndAuthClusterTest extends 
ItConnectToClusterTestBase {
         // And last connected URL is equal to the default URL
         stateConfigProvider.config = 
TestStateConfigHelper.createLastConnectedSslDefault();
 
-        // And answer to the reconnect question is "y", to the SSL 
configuration question is "y",
-        // trust store path and password are provided and key store is not 
configured
-        bindAnswers("y", NodeConfig.resolvedTruststorePath, 
NodeConfig.trustStorePassword, "n", "y", "admin", "password");
+        // And answer to the SSL configuration question is "y", trust store 
path and password are provided
+        // and answer to key store configuration is "n", answer to auth 
configuration is "y", username and password is provided
+        // and answer to save authentication is "y"
+        bindAnswers("y", NodeConfig.resolvedTruststorePath, 
NodeConfig.trustStorePassword, "n", "y", "admin", "password", "y");
 
         // When connect with auth parameters
         execute("connect", "--username", "admin", "--password", 
"wrong-password");
@@ -298,10 +300,11 @@ class ItConnectToSslAndAuthClusterTest extends 
ItConnectToClusterTestBase {
         // And last connected URL is equal to the default URL
         stateConfigProvider.config = 
TestStateConfigHelper.createLastConnectedSslDefault();
 
-        // And answer to the reconnect question is "y", to the SSL 
configuration question is "y",
-        // trust store path and password are provided and key store is not 
configured
+        // And answer to the SSL configuration question is "y", trust store 
path and password are provided
+        // and answer to key store configuration is "n", answer to auth 
configuration is "y", username and password is provided
+        // and answer to save authentication is "y"
         //ToDo: check question as well IGNITE-20324
-        bindAnswers("y", NodeConfig.resolvedTruststorePath, 
NodeConfig.trustStorePassword, "n", "y", "admin", "password");
+        bindAnswers("y", NodeConfig.resolvedTruststorePath, 
NodeConfig.trustStorePassword, "n", "y", "admin", "password", "y");
 
         // When connect with auth parameters
         execute("connect");
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
index 739c784492..45338cce21 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
@@ -17,11 +17,14 @@
 
 package org.apache.ignite.internal.cli.call.connect;
 
+import static 
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion.askQuestionToStoreCredentials;
+import static 
org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_USERNAME;
 import static org.apache.ignite.internal.util.StringUtils.nullOrBlank;
 
 import io.micronaut.http.HttpStatus;
 import jakarta.inject.Singleton;
 import java.util.Objects;
+import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
@@ -54,16 +57,25 @@ public class ConnectCall implements Call<ConnectCallInput, 
String> {
 
     private final ConnectionChecker connectionChecker;
 
+    private final ConfigManagerProvider configManagerProvider;
+
     /**
      * Constructor.
      */
-    public ConnectCall(Session session, ApiClientFactory clientFactory, 
EventPublisher eventPublisher,
-            ConnectSuccessCall connectSuccessCall, ConnectionChecker 
connectionChecker) {
+    public ConnectCall(
+            Session session,
+            ApiClientFactory clientFactory,
+            EventPublisher eventPublisher,
+            ConnectSuccessCall connectSuccessCall,
+            ConnectionChecker connectionChecker,
+            ConfigManagerProvider configManagerProvider
+    ) {
         this.session = session;
         this.clientFactory = clientFactory;
         this.eventPublisher = eventPublisher;
         this.connectSuccessCall = connectSuccessCall;
         this.connectionChecker = connectionChecker;
+        this.configManagerProvider = configManagerProvider;
     }
 
     @Override
@@ -71,15 +83,21 @@ public class ConnectCall implements Call<ConnectCallInput, 
String> {
         String nodeUrl = input.url();
         SessionInfo sessionInfo = session.info();
         if (sessionInfo != null && Objects.equals(sessionInfo.nodeUrl(), 
nodeUrl)) {
-            MessageUiComponent message = MessageUiComponent.fromMessage("You 
are already connected to %s", UiElements.url(nodeUrl));
-            return DefaultCallOutput.success(message.render());
+            // This username will be used for connect by the connection 
checker.
+            String username = input.username() != null
+                    ? input.username()
+                    : 
configManagerProvider.get().getCurrentProperty(BASIC_AUTHENTICATION_USERNAME.value());
+            if (Objects.equals(sessionInfo.username(), username)) {
+                MessageUiComponent message = 
MessageUiComponent.fromMessage("You are already connected to %s", 
UiElements.url(nodeUrl));
+                return DefaultCallOutput.success(message.render());
+            }
         }
         try {
             // Try without authentication first to check whether the 
authentication is enabled on the cluster.
             sessionInfo = connectWithoutAuthentication(nodeUrl);
             if (sessionInfo == null) {
                 // Try with authentication
-                sessionInfo =  connectionChecker.checkConnection(input);
+                sessionInfo = connectionChecker.checkConnection(input);
                 if (!nullOrBlank(input.username()) && 
!nullOrBlank(input.password())) {
                     // Use current credentials as default for api clients
                     ApiClientSettings clientSettings = 
ApiClientSettings.builder()
@@ -96,6 +114,7 @@ public class ConnectCall implements Call<ConnectCallInput, 
String> {
                                 sessionInfo.nodeUrl()));
             }
 
+            askQuestionToStoreCredentials(configManagerProvider.get(), 
input.username(), input.password());
             return connectSuccessCall.execute(sessionInfo);
         } catch (Exception e) {
             if (session.info() != null) {
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCallInput.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCallInput.java
index 207eea1e7d..e1460b310c 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCallInput.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCallInput.java
@@ -37,7 +37,7 @@ public class ConnectCallInput implements CallInput {
         this.password = password;
     }
 
-    String url() {
+    public String url() {
         return url;
     }
 
@@ -47,7 +47,7 @@ public class ConnectCallInput implements CallInput {
      * @return username
      */
     @Nullable
-    String username() {
+    public String username() {
         return username;
     }
 
@@ -57,7 +57,7 @@ public class ConnectCallInput implements CallInput {
      * @return password
      */
     @Nullable
-    String password() {
+    public String password() {
         return password;
     }
 
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectWizardCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectWizardCall.java
index adfa179595..9e312de69d 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectWizardCall.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectWizardCall.java
@@ -17,12 +17,14 @@
 
 package org.apache.ignite.internal.cli.call.connect;
 
+import static 
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion.askQuestionOnAuthError;
 import static 
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion.askQuestionOnSslError;
+import static 
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion.askQuestionToStoreCredentials;
 
 import io.micronaut.http.HttpStatus;
 import jakarta.inject.Singleton;
 import javax.net.ssl.SSLException;
-import 
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion;
+import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
@@ -44,13 +46,21 @@ public class ConnectWizardCall implements 
Call<ConnectCallInput, String> {
 
     private final ConnectionChecker connectionChecker;
 
+    private final ConfigManagerProvider configManagerProvider;
+
     /**
      * Constructor.
      */
-    public ConnectWizardCall(ConnectCall connectCall, ConnectionChecker 
connectionChecker, ConnectSuccessCall connectSuccessCall) {
+    public ConnectWizardCall(
+            ConnectCall connectCall,
+            ConnectionChecker connectionChecker,
+            ConnectSuccessCall connectSuccessCall,
+            ConfigManagerProvider configManagerProvider
+    ) {
         this.connectCall = connectCall;
         this.connectionChecker = connectionChecker;
         this.connectSuccessCall = connectSuccessCall;
+        this.configManagerProvider = configManagerProvider;
     }
 
     @Override
@@ -84,6 +94,7 @@ public class ConnectWizardCall implements 
Call<ConnectCallInput, String> {
                 SessionInfo sessionInfo = 
connectionChecker.checkConnection(input, result.value());
                 connectionChecker.saveSettings(input, result.value());
 
+                askQuestionToStoreCredentials(configManagerProvider.get(), 
input.username(), input.password());
                 return connectSuccessCall.execute(sessionInfo);
             } catch (ApiException exception) {
                 Throwable apiCause = exception.getCause();
@@ -106,16 +117,19 @@ public class ConnectWizardCall implements 
Call<ConnectCallInput, String> {
     }
 
     private CallOutput<String> configureAuth(ConnectCallInput input, 
CallOutput<String> output) {
-        Flowable<AuthConfig> result = 
ConnectToClusterQuestion.askQuestionOnAuthError().start(Flowable.empty());
+        Flowable<AuthConfig> result = 
askQuestionOnAuthError().start(Flowable.empty());
         if (result.hasResult()) {
+            String username = result.value().username();
+            String password = result.value().password();
             ConnectCallInput connectCallInput = ConnectCallInput.builder()
                     .url(input.url())
-                    .username(result.value().username())
-                    .password(result.value().password())
+                    .username(username)
+                    .password(password)
                     .build();
             try {
                 SessionInfo sessionInfo = 
connectionChecker.checkConnection(connectCallInput);
                 connectionChecker.saveSettings(connectCallInput, null);
+                askQuestionToStoreCredentials(configManagerProvider.get(), 
username, password);
                 return connectSuccessCall.execute(sessionInfo);
             } catch (ApiException e) {
                 return DefaultCallOutput.failure(new IgniteCliApiException(e, 
input.url()));
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectionChecker.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectionChecker.java
index 3e7479773a..ae4ff40ce3 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectionChecker.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectionChecker.java
@@ -160,10 +160,5 @@ public class ConnectionChecker {
                 manager.setProperty(REST_KEY_STORE_PASSWORD.value(), 
sslConfig.keyStorePassword());
             }
         }
-
-        if (!nullOrBlank(callInput.username()) && 
!nullOrBlank(callInput.password())) {
-            manager.setProperty(BASIC_AUTHENTICATION_USERNAME.value(), 
callInput.username());
-            manager.setProperty(BASIC_AUTHENTICATION_PASSWORD.value(), 
callInput.password());
-        }
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectOptions.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectOptions.java
index b6c6827918..ff6080c341 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectOptions.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectOptions.java
@@ -31,12 +31,12 @@ import picocli.CommandLine.Option;
  */
 public class ConnectOptions {
 
-    @Option(names = {USERNAME_OPTION,
-            USERNAME_OPTION_SHORT}, description = USERNAME_OPTION_DESC, 
required = true)
+    @Option(names = {USERNAME_OPTION, USERNAME_OPTION_SHORT}, description = 
USERNAME_OPTION_DESC,
+            required = true, defaultValue = Option.NULL_VALUE)
     private String username;
 
-    @Option(names = {PASSWORD_OPTION,
-            PASSWORD_OPTION_SHORT}, description = PASSWORD_OPTION_DESC, 
required = true)
+    @Option(names = {PASSWORD_OPTION, PASSWORD_OPTION_SHORT}, description = 
PASSWORD_OPTION_DESC,
+            required = true, defaultValue = Option.NULL_VALUE)
     private String password;
 
     public String username() {
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
index 964ca46e72..01d221bc7f 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/connect/ConnectReplCommand.java
@@ -18,16 +18,16 @@
 package org.apache.ignite.internal.cli.commands.connect;
 
 import static 
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_URL_KEY;
-import static 
org.apache.ignite.internal.cli.commands.Options.Constants.NODE_URL_OR_NAME_DESC;
+import static 
org.apache.ignite.internal.cli.commands.Options.Constants.NODE_URL_OPTION_DESC;
 
 import jakarta.inject.Inject;
+import java.net.URL;
 import org.apache.ignite.internal.cli.call.connect.ConnectCallInput;
 import org.apache.ignite.internal.cli.call.connect.ConnectWizardCall;
 import org.apache.ignite.internal.cli.commands.BaseCommand;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
 import 
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion;
+import org.apache.ignite.internal.cli.core.converters.UrlConverter;
 import org.apache.ignite.internal.cli.core.flow.builder.Flows;
-import org.jetbrains.annotations.Nullable;
 import picocli.CommandLine.ArgGroup;
 import picocli.CommandLine.Command;
 import picocli.CommandLine.Parameters;
@@ -39,8 +39,8 @@ import picocli.CommandLine.Parameters;
 public class ConnectReplCommand extends BaseCommand implements Runnable {
 
     /** Node URL option. */
-    @Parameters(description = NODE_URL_OR_NAME_DESC, descriptionKey = 
CLUSTER_URL_KEY)
-    private NodeNameOrUrl nodeNameOrUrl;
+    @Parameters(description = NODE_URL_OPTION_DESC, descriptionKey = 
CLUSTER_URL_KEY, converter = UrlConverter.class)
+    private URL nodeUrl;
 
     @ArgGroup(exclusive = false)
     private ConnectOptions connectOptions;
@@ -54,30 +54,18 @@ public class ConnectReplCommand extends BaseCommand 
implements Runnable {
     /** {@inheritDoc} */
     @Override
     public void run() {
-        question.askQuestionIfConnected(nodeNameOrUrl.stringUrl())
-                .map(this::connectCallInput)
+        question.askQuestionIfConnected(connectCallInput(nodeUrl.toString()))
                 .then(Flows.fromCall(connectCall))
-                .onSuccess(() -> 
question.askQuestionToStoreCredentials(username(connectOptions), 
password(connectOptions)))
                 .verbose(verbose)
                 .print()
                 .start();
     }
 
-    @Nullable
-    private String username(ConnectOptions connectOptions) {
-        return connectOptions != null ? connectOptions.username() : null;
-    }
-
-    @Nullable
-    private String password(ConnectOptions connectOptions) {
-        return connectOptions != null ? connectOptions.password() : null;
-    }
-
     private ConnectCallInput connectCallInput(String nodeUrl) {
         return ConnectCallInput.builder()
                 .url(nodeUrl)
-                .username(username(connectOptions))
-                .password(password(connectOptions))
+                .username(connectOptions != null ? connectOptions.username() : 
null)
+                .password(connectOptions != null ? connectOptions.password() : 
null)
                 .build();
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeNameOrUrl.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeNameOrUrl.java
deleted file mode 100644
index 8ec6a73209..0000000000
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeNameOrUrl.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.cli.commands.node;
-
-import java.net.URL;
-
-/** Node URL. */
-public class NodeNameOrUrl {
-    private final URL nodeUrl;
-
-    public NodeNameOrUrl(URL nodeUrl) {
-        this.nodeUrl = nodeUrl;
-    }
-
-    public String stringUrl() {
-        return nodeUrl.toString();
-    }
-}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/questions/ConnectToClusterQuestion.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/questions/ConnectToClusterQuestion.java
index 54af28a023..37abc792ec 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/questions/ConnectToClusterQuestion.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/questions/ConnectToClusterQuestion.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.cli.commands.questions;
 import static 
org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_PASSWORD;
 import static 
org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_USERNAME;
 import static 
org.apache.ignite.internal.cli.core.style.component.QuestionUiComponent.fromYesNoQuestion;
+import static 
org.apache.ignite.internal.cli.core.style.element.UiElements.username;
 import static org.apache.ignite.internal.util.StringUtils.nullOrBlank;
 
 import jakarta.inject.Inject;
@@ -30,6 +31,7 @@ import 
org.apache.ignite.internal.cli.call.connect.ConnectCallInput;
 import org.apache.ignite.internal.cli.call.connect.ConnectWizardCall;
 import org.apache.ignite.internal.cli.call.connect.SslConfig;
 import org.apache.ignite.internal.cli.config.CliConfigKeys;
+import org.apache.ignite.internal.cli.config.ConfigManager;
 import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
 import org.apache.ignite.internal.cli.config.StateConfigProvider;
 import org.apache.ignite.internal.cli.core.flow.Flow;
@@ -97,41 +99,64 @@ public class ConnectToClusterQuestion {
     }
 
     /**
-     * Ask if the user really wants to connect if we are already connected and 
the URL is different.
+     * Ask if the user really wants to connect if we are already connected and 
the URL or authentication parameters are different.
      *
-     * @param nodeUrl Node URL.
-     * @return {@link FlowBuilder} instance which provides the node URL if we 
are not connected or connected to different URL or interrupts
-     *         if user's answer is negative.
+     * @param input Node URL and authentication parameters.
+     * @return {@link FlowBuilder} instance which provides the connect call 
input if we are not connected or user agreed to reconnect or
+     *         interrupts if user's answer is negative.
      */
-    public FlowBuilder<Void, String> askQuestionIfConnected(String nodeUrl) {
+    public FlowBuilder<Void, ConnectCallInput> 
askQuestionIfConnected(ConnectCallInput input) {
         SessionInfo sessionInfo = session.info();
-        if (sessionInfo != null && !Objects.equals(sessionInfo.nodeUrl(), 
nodeUrl)) {
-            QuestionUiComponent question = fromYesNoQuestion(
-                    "You are already connected to the %s, do you want to 
connect to the %s?",
-                    UiElements.url(sessionInfo.nodeUrl()), 
UiElements.url(nodeUrl)
-            );
-            return Flows.acceptQuestion(question, () -> nodeUrl);
+        if (sessionInfo != null) {
+            if (!Objects.equals(sessionInfo.nodeUrl(), input.url())) {
+                return Flows.acceptQuestion(fromYesNoQuestion(
+                        "You are already connected to the %s, do you want to 
connect to the %s?",
+                        UiElements.url(sessionInfo.nodeUrl()), 
UiElements.url(input.url())
+                ), () -> input);
+            }
+
+            String oldUsername = sessionInfo.username();
+            // This username will be used for connect by the connection 
checker.
+            String newUsername = input.username() != null
+                    ? input.username()
+                    : 
configManagerProvider.get().getCurrentProperty(BASIC_AUTHENTICATION_USERNAME.value());
+
+            if (newUsername != null) {
+                if (oldUsername == null) {
+                    return Flows.acceptQuestion(fromYesNoQuestion(
+                            "You are already connected to the %s, do you want 
to connect as %s?",
+                            UiElements.url(sessionInfo.nodeUrl()), 
username(newUsername)
+                    ), () -> input);
+                }
+                if (!oldUsername.equals(newUsername)) {
+                    return Flows.acceptQuestion(fromYesNoQuestion(
+                            "You are already connected to the %s as %s, do you 
want to connect as %s?",
+                            UiElements.url(sessionInfo.nodeUrl()), 
username(oldUsername), username(newUsername)
+                    ), () -> input);
+                }
+            }
         }
-        return Flows.from(nodeUrl);
+        return Flows.from(input);
     }
 
     /**
      * Ask if the user wants to store credentials in config.
      *
+     * @param configManager Config manager.
      * @param username username.
      * @param password password
      */
-    public void askQuestionToStoreCredentials(@Nullable String username, 
@Nullable String password) {
+    public static void askQuestionToStoreCredentials(ConfigManager 
configManager, @Nullable String username, @Nullable String password) {
         if (!nullOrBlank(username) && !nullOrBlank(password)) {
-            String storedUsername = 
configManagerProvider.get().getCurrentProperty(BASIC_AUTHENTICATION_USERNAME.value());
-            String storedPassword = 
configManagerProvider.get().getCurrentProperty(BASIC_AUTHENTICATION_PASSWORD.value());
+            String storedUsername = 
configManager.getCurrentProperty(BASIC_AUTHENTICATION_USERNAME.value());
+            String storedPassword = 
configManager.getCurrentProperty(BASIC_AUTHENTICATION_PASSWORD.value());
 
             // Ask question only if cli config has different values.
             if (!username.equals(storedUsername) || 
!password.equals(storedPassword)) {
                 QuestionUiComponent question = fromYesNoQuestion("Remember 
current credentials?");
                 Flows.acceptQuestion(question, () -> {
-                    
configManagerProvider.get().setProperty(BASIC_AUTHENTICATION_USERNAME.value(), 
username);
-                    
configManagerProvider.get().setProperty(BASIC_AUTHENTICATION_PASSWORD.value(), 
password);
+                    
configManager.setProperty(BASIC_AUTHENTICATION_USERNAME.value(), username);
+                    
configManager.setProperty(BASIC_AUTHENTICATION_PASSWORD.value(), password);
                     return "Config saved";
                 }).print().start();
             }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/NodeNameOrUrlConverter.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/NodeNameOrUrlConverter.java
deleted file mode 100644
index 558a4fbb8b..0000000000
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/converters/NodeNameOrUrlConverter.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.cli.core.converters;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.regex.Pattern;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
-import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
-import picocli.CommandLine;
-import picocli.CommandLine.TypeConversionException;
-
-/** Converter for {@link NodeNameOrUrl}. */
-public class NodeNameOrUrlConverter implements 
CommandLine.ITypeConverter<NodeNameOrUrl> {
-
-    private static final Pattern URL_PATTERN = Pattern.compile("^.*[/:].*");
-
-    private final NodeNameRegistry nodeNameRegistry;
-
-    public NodeNameOrUrlConverter(NodeNameRegistry nodeNameRegistry) {
-        this.nodeNameRegistry = nodeNameRegistry;
-    }
-
-    private static URL stringToUrl(String str) {
-        try {
-            return new URL(str);
-        } catch (MalformedURLException e) {
-            throw new TypeConversionException("Invalid URL '" + str + "' (" + 
e.getMessage() + ")");
-        }
-    }
-
-    @Override
-    public NodeNameOrUrl convert(String input) throws Exception {
-        boolean isUrl = URL_PATTERN.matcher(input).matches();
-        if (isUrl) {
-            return new NodeNameOrUrl(stringToUrl(input));
-        } else {
-            return new 
NodeNameOrUrl(stringToUrl(findNodeUrlByNodeName(input)));
-        }
-    }
-
-    private String findNodeUrlByNodeName(String name) {
-        return nodeNameRegistry.nodeUrlByName(name)
-                .orElseThrow(() -> new TypeConversionException("Node " + name 
+ " not found. Provide valid name or use URL"));
-    }
-}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/Session.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/Session.java
index 0f5ec6b008..313cdcbd0d 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/Session.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/Session.java
@@ -19,7 +19,6 @@ package org.apache.ignite.internal.cli.core.repl;
 
 import jakarta.inject.Singleton;
 import java.util.concurrent.atomic.AtomicReference;
-import org.apache.ignite.internal.cli.core.exception.ConnectionException;
 import org.apache.ignite.internal.cli.event.ConnectionEventListener;
 
 /**
@@ -30,19 +29,14 @@ public class Session implements ConnectionEventListener {
 
     private final AtomicReference<SessionInfo> info = new AtomicReference<>();
 
-    public Session() {
-    }
-
     @Override
     public void onConnect(SessionInfo sessionInfo) {
-        if (!info.compareAndSet(null, sessionInfo)) {
-            throw new ConnectionException("Already connected to " + 
info.get().nodeUrl());
-        }
+        info.set(sessionInfo);
     }
 
     @Override
     public void onDisconnect() {
-        info.getAndSet(null);
+        info.set(null);
     }
 
     /** Returns {@link SessionInfo}. */
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
index ae54ee9b1a..f2fd184338 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterActivationPoint.java
@@ -103,12 +103,6 @@ public class DynamicCompleterActivationPoint {
                         .exclusiveEnableOptions().build(),
                 nodeNameDynamicCompleterFactory
         );
-        registry.register(
-                CompleterConf.builder()
-                        .command("connect")
-                        .singlePositionalParameter().build(),
-                nodeNameDynamicCompleterFactory
-        );
         registry.register(
                 CompleterConf.builder()
                         .command("cluster", "init")
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsider.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsider.java
index d963fb7793..2d618f45b7 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsider.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsider.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.cli.core.repl.completer;
 import java.util.ArrayList;
 import java.util.List;
 import org.apache.ignite.internal.cli.commands.TopLevelCliReplCommand;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
 import picocli.CommandLine;
 import picocli.CommandLine.MissingParameterException;
 import picocli.CommandLine.Model.ArgSpec;
@@ -33,7 +32,6 @@ public class DynamicCompletionInsider {
 
     public DynamicCompletionInsider() {
         commandLine = new CommandLine(TopLevelCliReplCommand.class);
-        commandLine.registerConverter(NodeNameOrUrl.class, value -> null);
     }
 
     private static String[] trim(String[] typedWords) {
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
index cf06e43725..d9be1fd8cf 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
@@ -22,9 +22,7 @@ import java.nio.file.Paths;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Supplier;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
 import org.apache.ignite.internal.cli.config.StateFolderProvider;
-import org.apache.ignite.internal.cli.core.converters.NodeNameOrUrlConverter;
 import org.apache.ignite.internal.cli.core.exception.ExceptionHandlers;
 import 
org.apache.ignite.internal.cli.core.exception.handler.PicocliExecutionExceptionHandler;
 import 
org.apache.ignite.internal.cli.core.exception.handler.ReplExceptionHandlers;
@@ -40,7 +38,6 @@ import 
org.apache.ignite.internal.cli.core.repl.completer.filter.NonRepeatableOp
 import 
org.apache.ignite.internal.cli.core.repl.completer.filter.ShortOptionsFilter;
 import 
org.apache.ignite.internal.cli.core.repl.context.CommandLineContextProvider;
 import org.apache.ignite.internal.cli.core.repl.expander.NoopExpander;
-import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
 import org.jline.console.impl.SystemRegistryImpl;
 import org.jline.reader.Completer;
 import org.jline.reader.LineReader;
@@ -73,19 +70,15 @@ public class ReplExecutorImpl implements ReplExecutor {
 
     private final Terminal terminal;
 
-    private final NodeNameRegistry nodeNameRegistry;
-
     /**
      * Constructor.
      *
      * @param commandsFactory picocli commands factory.
      * @param terminal terminal instance.
-     * @param nodeNameRegistry node name registry.
      */
-    public ReplExecutorImpl(PicocliCommandsFactory commandsFactory, Terminal 
terminal, NodeNameRegistry nodeNameRegistry) {
+    public ReplExecutorImpl(PicocliCommandsFactory commandsFactory, Terminal 
terminal) {
         this.factory = commandsFactory;
         this.terminal = terminal;
-        this.nodeNameRegistry = nodeNameRegistry;
     }
 
     private static void createTailTipWidgets(SystemRegistryImpl registry, 
LineReader reader) {
@@ -185,7 +178,6 @@ public class ReplExecutorImpl implements ReplExecutor {
         }
         CommandLineContextProvider.setCmd(cmd);
         cmd.setExecutionExceptionHandler(new 
PicocliExecutionExceptionHandler(exceptionHandlers));
-        cmd.registerConverter(NodeNameOrUrl.class, new 
NodeNameOrUrlConverter(nodeNameRegistry));
         cmd.setTrimQuotes(true);
 
         DynamicCompleterRegistry completerRegistry = 
factory.create(DynamicCompleterRegistry.class);
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProviderImpl.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProviderImpl.java
index 8a66b5a485..93286ebfe2 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProviderImpl.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProviderImpl.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.cli.core.repl.executor;
 import io.micronaut.configuration.picocli.MicronautFactory;
 import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
 import org.jline.terminal.Terminal;
 import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
 
@@ -34,12 +33,9 @@ public class ReplExecutorProviderImpl implements 
ReplExecutorProvider {
     @Inject
     private Terminal terminal;
 
-    @Inject
-    private NodeNameRegistry nodeNameRegistry;
-
     @Override
     public ReplExecutor get() {
-        return new ReplExecutorImpl(factory, terminal, nodeNameRegistry);
+        return new ReplExecutorImpl(factory, terminal);
     }
 
     public void injectFactory(MicronautFactory micronautFactory) {
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupport.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupport.java
index 0ce4ff8d35..fadd6d355e 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupport.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupport.java
@@ -56,6 +56,7 @@ public final class AnsiStringSupport {
         }
 
         /** Marks given text with the configured before style. */
+        @Override
         public String mark(String textToMark) {
             if (style == Style.BOLD) {
                 return String.format("@|fg(%d),bold %s|@", color.code, 
textToMark);
@@ -74,6 +75,7 @@ public final class AnsiStringSupport {
             this.value = value;
         }
 
+        @Override
         public String mark(String textToMark) {
             return String.format("@|%s %s|@", value, textToMark);
         }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiElements.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiElements.java
index 962212a96a..22a56834ce 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiElements.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiElements.java
@@ -57,4 +57,9 @@ public class UiElements {
     public static UiElement yesNo() {
         return new MarkedUiElement("[Y/n]", fg(Color.GRAY));
     }
+
+    /** Username UI element. */
+    public static UiElement username(String content) {
+        return () -> content;
+    }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiString.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiString.java
index 47a4451214..ba4baa1e10 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiString.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/element/UiString.java
@@ -27,7 +27,7 @@ public class UiString {
             return template;
         }
 
-        Object[] elementsAsObjects = 
Arrays.stream(elements).map(Object.class::cast).toArray();
+        Object[] elementsAsObjects = 
Arrays.stream(elements).map(UiElement::represent).toArray();
 
         return String.format(template, elementsAsObjects);
     }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
index aaee3f48a6..3e5ee65bf4 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/CliCommandTestBase.java
@@ -26,8 +26,6 @@ import jakarta.inject.Inject;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Arrays;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
-import org.apache.ignite.internal.cli.core.converters.NodeNameOrUrlConverter;
 import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
 import org.junit.jupiter.api.BeforeEach;
 import picocli.CommandLine;
@@ -53,8 +51,7 @@ public abstract class CliCommandTestBase {
 
     @BeforeEach
     public void setUp() {
-        cmd = new CommandLine(getCommandClass(), new MicronautFactory(context))
-                .registerConverter(NodeNameOrUrl.class, new 
NodeNameOrUrlConverter(nodeNameRegistry));
+        cmd = new CommandLine(getCommandClass(), new 
MicronautFactory(context));
         sout = new StringWriter();
         serr = new StringWriter();
         cmd.setOut(new PrintWriter(sout));
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/UrlOptionsNegativeTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/UrlOptionsNegativeTest.java
index 49aeda04a8..3cac6b330a 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/UrlOptionsNegativeTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/UrlOptionsNegativeTest.java
@@ -54,7 +54,6 @@ import 
org.apache.ignite.internal.cli.commands.cluster.unit.ClusterUnitUndeployC
 import 
org.apache.ignite.internal.cli.commands.cluster.unit.ClusterUnitUndeployReplCommand;
 import org.apache.ignite.internal.cli.commands.connect.ConnectCommand;
 import org.apache.ignite.internal.cli.commands.connect.ConnectReplCommand;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
 import 
org.apache.ignite.internal.cli.commands.node.config.NodeConfigShowCommand;
 import 
org.apache.ignite.internal.cli.commands.node.config.NodeConfigShowReplCommand;
 import 
org.apache.ignite.internal.cli.commands.node.config.NodeConfigUpdateCommand;
@@ -71,7 +70,6 @@ import 
org.apache.ignite.internal.cli.commands.node.status.NodeStatusCommand;
 import 
org.apache.ignite.internal.cli.commands.node.status.NodeStatusReplCommand;
 import org.apache.ignite.internal.cli.commands.node.unit.NodeUnitListCommand;
 import 
org.apache.ignite.internal.cli.commands.node.unit.NodeUnitListReplCommand;
-import org.apache.ignite.internal.cli.core.converters.NodeNameOrUrlConverter;
 import 
org.apache.ignite.internal.cli.core.repl.context.CommandLineContextProvider;
 import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
 import org.apache.ignite.internal.testframework.WorkDirectory;
@@ -126,8 +124,7 @@ public class UrlOptionsNegativeTest {
     private void setUp(Class<?> cmdClass) {
         
configManagerProvider.setConfigFile(TestConfigManagerHelper.createSectionWithDefaultProfileConfig());
         MicronautFactory factory = new MicronautFactory(context);
-        cmd = new CommandLine(cmdClass, factory)
-                .registerConverter(NodeNameOrUrl.class, new 
NodeNameOrUrlConverter(nodeNameRegistry));
+        cmd = new CommandLine(cmdClass, factory);
         CommandLineContextProvider.setCmd(cmd);
         sout = new StringWriter();
         serr = new StringWriter();
@@ -314,7 +311,7 @@ public class UrlOptionsNegativeTest {
 
         assertAll(
                 this::assertOutputIsEmpty,
-                () -> assertErrOutputContains("Missing required parameter: 
'<nodeNameOrUrl>'")
+                () -> assertErrOutputContains("Missing required parameter: 
'<nodeUrl>'")
         );
     }
 
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommandTest.java
index bee56dfc31..b2d44dd0fe 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommandTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/connect/ConnectCommandTest.java
@@ -31,14 +31,13 @@ class ConnectCommandTest extends CliCommandTestBase {
     }
 
     @Test
-    @DisplayName("Should throw error if provided an unknown node name")
-    void unknownNodeName() {
+    void invalidNodeUrl() {
         execute("nodeName");
 
         assertAll(
                 () -> assertExitCodeIs(2),
                 this::assertOutputIsEmpty,
-                () -> assertErrOutputContains("Node nodeName not found. 
Provide valid name or use URL")
+                () -> assertErrOutputContains("Invalid URL 'nodeName' (no 
protocol: nodeName)")
         );
     }
 
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistryTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistryTest.java
index d5046002f5..26ab52ece3 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistryTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompleterRegistryTest.java
@@ -324,22 +324,22 @@ class DynamicCompleterRegistryTest {
         // Given completer for single positional parameter
         registry.register(
                 CompleterConf.builder()
-                        .command("connect") // should be real commands
+                        .command("cluster", "config", "show") // should be 
real commands
                         .singlePositionalParameter()
                         .build(),
                 words -> completer1
         );
 
         // When there is no positional argument typed
-        List<DynamicCompleter> connectCompleter = 
registry.findCompleters(words("connect"));
+        List<DynamicCompleter> completers = 
registry.findCompleters(words("cluster", "config", "show"));
 
         // Then completer is returned
-        assertThat(connectCompleter, 
both(hasSize(1)).and(containsInAnyOrder(completer1)));
+        assertThat(completers, contains(completer1));
 
         // When there is one positional argument typed
-        List<DynamicCompleter> connectCompleterWithPositional = 
registry.findCompleters(words("connect", "arg1", ""));
+        List<DynamicCompleter> completersWithPositional = 
registry.findCompleters(words("cluster", "config", "show", "arg1", ""));
 
         // Then completer is not returned
-        assertThat(connectCompleterWithPositional, hasSize(0));
+        assertThat(completersWithPositional, is(empty()));
     }
 }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsiderTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsiderTest.java
index ae47b82a33..e1fb2e45e3 100644
--- 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsiderTest.java
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/repl/completer/DynamicCompletionInsiderTest.java
@@ -31,12 +31,6 @@ class DynamicCompletionInsiderTest {
 
     private static Stream<Arguments> typedWordsSource() {
         return Stream.of(
-                // connect command needs a parameter
-                Arguments.of(new String[]{"connect"}, false),
-                // technically, we have a parameter, but it could be typing in 
progress
-                Arguments.of(new String[]{"connect", "node"}, false),
-                // here we have completed parameter
-                Arguments.of(new String[]{"connect", "node", ""}, true),
                 Arguments.of(new String[]{"cluster", "unit", "undeploy"}, 
false),
                 Arguments.of(new String[]{"cluster", "unit", "undeploy", 
"unit.id", "--version"}, true),
                 Arguments.of(new String[]{"cluster", "unit", "undeploy", 
"unit.id", "--version", "1.0.0"}, true),
diff --git 
a/modules/cli/src/testFixtures/resources/cluster-configuration-with-enabled-auth.conf
 
b/modules/cli/src/testFixtures/resources/cluster-configuration-with-enabled-auth.conf
index 9931ed53b3..f9461400ef 100644
--- 
a/modules/cli/src/testFixtures/resources/cluster-configuration-with-enabled-auth.conf
+++ 
b/modules/cli/src/testFixtures/resources/cluster-configuration-with-enabled-auth.conf
@@ -7,6 +7,12 @@ security: {
         type: basic,
         username: admin,
         password: password
+      },
+      {
+        name: basic1,
+        type: basic,
+        username: admin1,
+        password: password
       }
     ]
   }

Reply via email to