You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ap...@apache.org on 2023/03/29 10:46:22 UTC
[ignite-3] branch main updated: IGNITE-18607: add basic auth to the cli (#1821)
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 c2f8f54bb2 IGNITE-18607: add basic auth to the cli (#1821)
c2f8f54bb2 is described below
commit c2f8f54bb234388ba45daf758432a29fd0893214
Author: Ivan Gagarkin <ga...@gmail.com>
AuthorDate: Wed Mar 29 14:46:15 2023 +0400
IGNITE-18607: add basic auth to the cli (#1821)
---
.../testframework/IntegrationTestBase.java | 4 +
.../commands/cluster/init/ItClusterInitTest.java | 88 ++++++++++
.../internal/rest/ItGeneratedRestClientTest.java | 2 +-
.../cluster/AuthenticationConfigConverter.java | 64 ++++++++
.../internal/cli/call/cluster/ClusterInitCall.java | 6 +-
.../cli/call/cluster/ClusterInitCallInput.java | 48 +++++-
.../cli/call/cluster/status/ClusterStatusCall.java | 2 +-
.../call/cluster/topology/LogicalTopologyCall.java | 2 +-
.../cluster/topology/PhysicalTopologyCall.java | 2 +-
.../call/configuration/ClusterConfigShowCall.java | 2 +-
.../configuration/ClusterConfigUpdateCall.java | 2 +-
.../cli/call/configuration/NodeConfigShowCall.java | 2 +-
.../call/configuration/NodeConfigUpdateCall.java | 2 +-
.../internal/cli/call/connect/ConnectCall.java | 2 +-
.../call/node/metric/NodeMetricSetListCall.java | 2 +-
.../node/metric/NodeMetricSourceEnableCall.java | 2 +-
.../call/node/metric/NodeMetricSourceListCall.java | 2 +-
.../cli/call/node/status/NodeStatusCall.java | 2 +-
.../cli/call/node/version/NodeVersionCall.java | 2 +-
.../internal/cli/call/unit/DeployUnitCall.java | 2 +-
.../cli/call/unit/DeployUnitCallFactory.java | 2 +-
.../internal/cli/call/unit/ListUnitCall.java | 2 +-
.../internal/cli/call/unit/UndeployUnitCall.java | 2 +-
.../internal/cli/call/unit/UnitStatusCall.java | 2 +-
.../ignite/internal/cli/commands/Options.java | 39 ++++-
.../cluster/init/AuthenticationOptions.java | 60 +++++++
.../commands/cluster/init/ClusterInitOptions.java | 14 +-
.../ignite/internal/cli/config/CliConfigKeys.java | 12 +-
.../internal/cli/config/ini/IniConfigManager.java | 12 ++
.../ignite/internal/cli/core/ApiClientBuilder.java | 145 -----------------
.../ignite/internal/cli/core/ApiClientFactory.java | 70 --------
.../handler/IgniteCliApiExceptionHandler.java | 34 ++--
.../repl/registry/impl/JdbcUrlRegistryImpl.java | 2 +-
.../internal/cli/core/rest/ApiClientFactory.java | 178 +++++++++++++++++++++
.../internal/cli/core/rest/ApiClientSettings.java | 103 ++++++++++++
.../cli/core/rest/ApiClientSettingsBuilder.java | 69 ++++++++
.../core/rest/BasicAuthenticationInterceptor.java | 44 +++++
.../internal/cli/IgniteCliInterfaceTest.java | 57 +++++++
38 files changed, 824 insertions(+), 263 deletions(-)
diff --git a/modules/api/src/testFixtures/java/org/apache/ignite/internal/testframework/IntegrationTestBase.java b/modules/api/src/testFixtures/java/org/apache/ignite/internal/testframework/IntegrationTestBase.java
index 76cc8cde9d..4e01de34bc 100644
--- a/modules/api/src/testFixtures/java/org/apache/ignite/internal/testframework/IntegrationTestBase.java
+++ b/modules/api/src/testFixtures/java/org/apache/ignite/internal/testframework/IntegrationTestBase.java
@@ -122,6 +122,10 @@ public class IntegrationTestBase extends BaseIgniteAbstractTest {
IgnitionManager.init(initParameters);
+ awaitClusterInitialized();
+ }
+
+ protected void awaitClusterInitialized() {
for (CompletableFuture<Ignite> future : futures) {
assertThat(future, willCompleteSuccessfully());
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/cluster/init/ItClusterInitTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/cluster/init/ItClusterInitTest.java
new file mode 100644
index 0000000000..c51fa03d77
--- /dev/null
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/cluster/init/ItClusterInitTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.cluster.init;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import org.apache.ignite.internal.cli.commands.CliCommandTestNotInitializedIntegrationBase;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link ClusterInitCommand}.
+ */
+public class ItClusterInitTest extends CliCommandTestNotInitializedIntegrationBase {
+
+ @Test
+ @DisplayName("Init cluster with basic authentication")
+ void initClusterWithBasicAuthentication() {
+
+ // when
+ execute("connect", "http://localhost:10301");
+
+ resetOutput();
+
+ execute(
+ "cluster", "init",
+ "--meta-storage-node", CLUSTER_NODE_NAMES.get(0),
+ "--cluster-name", "cluster",
+ "--auth-enabled",
+ "--basic-auth-login", "admin",
+ "--basic-auth-password", "password"
+ );
+
+ assertAll(
+ this::assertErrOutputIsEmpty,
+ () -> assertOutputContains("Cluster was initialized successfully")
+ );
+
+ // then
+ awaitClusterInitialized();
+
+ // basic authentication has been enabled
+ assertRestIsUnavailable();
+
+ // set basic authentication settings
+ execute("cli", "config", "set", "ignite.auth.basic.login=admin");
+ execute("cli", "config", "set", "ignite.auth.basic.password=password");
+
+ // REST is available
+ assertRestIsAvailable();
+ }
+
+ private void assertRestIsUnavailable() {
+ resetOutput();
+ execute("cluster", "config", "show");
+
+ assertAll(
+ () -> assertErrOutputContains("Authentication error"),
+ this::assertOutputIsEmpty
+ );
+ }
+
+ private void assertRestIsAvailable() {
+ resetOutput();
+ execute("cluster", "config", "show");
+
+ assertAll(
+ this::assertErrOutputIsEmpty,
+ this::assertOutputIsNotEmpty
+ );
+ }
+
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
index 13a48dfa01..2cd679b3a1 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
@@ -51,7 +51,7 @@ import java.util.stream.IntStream;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgnitionManager;
import org.apache.ignite.InitParameters;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.internal.testframework.WorkDirectory;
import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
import org.apache.ignite.internal.util.IgniteUtils;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/AuthenticationConfigConverter.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/AuthenticationConfigConverter.java
new file mode 100644
index 0000000000..8bdfd171be
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/AuthenticationConfigConverter.java
@@ -0,0 +1,64 @@
+/*
+ * 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.call.cluster;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.ignite.rest.client.model.AuthenticationConfig;
+import org.apache.ignite.rest.client.model.AuthenticationProviderConfig;
+import org.apache.ignite.security.BasicAuthenticationProviderConfig;
+import org.jetbrains.annotations.Nullable;
+
+final class AuthenticationConfigConverter {
+
+ private AuthenticationConfigConverter() {
+ }
+
+ @Nullable
+ static AuthenticationConfig toAuthenticationConfig(org.apache.ignite.security.AuthenticationConfig config) {
+ if (config == null) {
+ return null;
+ }
+
+ List<AuthenticationProviderConfig> providerConfigs = config.providers().stream()
+ .map(AuthenticationConfigConverter::toAuthenticationProviderConfigDto)
+ .collect(Collectors.toList());
+
+ return new AuthenticationConfig()
+ .enabled(config.enabled())
+ .providers(providerConfigs);
+ }
+
+ private static AuthenticationProviderConfig toAuthenticationProviderConfigDto(
+ org.apache.ignite.security.AuthenticationProviderConfig provider
+ ) {
+ switch (provider.type()) {
+ case BASIC:
+ BasicAuthenticationProviderConfig config =
+ (BasicAuthenticationProviderConfig) provider;
+
+ return new AuthenticationProviderConfig()
+ .type(config.type().toString())
+ .name(config.name())
+ .login(config.login())
+ .password(config.password());
+ default:
+ throw new IllegalArgumentException("Unexpected authentication provider type: " + provider.type());
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCall.java
index 1142c772f5..4d5e1afaa4 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCall.java
@@ -18,13 +18,12 @@
package org.apache.ignite.internal.cli.call.cluster;
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
import org.apache.ignite.internal.cli.core.call.Call;
import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.ClusterManagementApi;
import org.apache.ignite.rest.client.invoker.ApiException;
-import org.apache.ignite.rest.client.model.AuthenticationConfig;
import org.apache.ignite.rest.client.model.InitCommand;
/**
@@ -48,8 +47,7 @@ public class ClusterInitCall implements Call<ClusterInitCallInput, String> {
.metaStorageNodes(input.getMetaStorageNodes())
.cmgNodes(input.getCmgNodes())
.clusterName(input.getClusterName())
- //TODO support authentication in the CLI https://issues.apache.org/jira/browse/IGNITE-18607
- .authenticationConfig(new AuthenticationConfig().enabled(false))
+ .authenticationConfig(AuthenticationConfigConverter.toAuthenticationConfig(input.authenticationConfig()))
);
return DefaultCallOutput.success("Cluster was initialized successfully");
} catch (ApiException | IllegalArgumentException e) {
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
index fe911c7dda..cf80e12bcb 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
@@ -17,9 +17,15 @@
package org.apache.ignite.internal.cli.call.cluster;
+import static org.apache.ignite.internal.util.StringUtils.nullOrBlank;
+
import java.util.List;
+import org.apache.ignite.internal.cli.commands.cluster.init.AuthenticationOptions;
import org.apache.ignite.internal.cli.commands.cluster.init.ClusterInitOptions;
import org.apache.ignite.internal.cli.core.call.CallInput;
+import org.apache.ignite.security.AuthenticationConfig;
+import org.apache.ignite.security.AuthenticationProviderConfig;
+import org.apache.ignite.security.BasicAuthenticationProviderConfig;
/**
* Input for {@link ClusterInitCall}.
@@ -29,17 +35,19 @@ public class ClusterInitCallInput implements CallInput {
private final List<String> metaStorageNodes;
private final List<String> cmgNodes;
private final String clusterName;
+ private final AuthenticationConfig authenticationConfig;
private ClusterInitCallInput(
String clusterUrl,
List<String> metaStorageNodes,
List<String> cmgNodes,
- String clusterName
- ) {
+ String clusterName,
+ AuthenticationConfig authenticationConfig) {
this.clusterUrl = clusterUrl;
this.metaStorageNodes = metaStorageNodes;
this.cmgNodes = cmgNodes;
this.clusterName = clusterName;
+ this.authenticationConfig = authenticationConfig;
}
/**
@@ -86,6 +94,10 @@ public class ClusterInitCallInput implements CallInput {
return clusterName;
}
+ public AuthenticationConfig authenticationConfig() {
+ return authenticationConfig;
+ }
+
/**
* Builder for {@link ClusterInitCallInput}.
*/
@@ -98,6 +110,13 @@ public class ClusterInitCallInput implements CallInput {
private String clusterName;
+ private AuthenticationConfig authenticationConfig;
+
+ public ClusterInitCallInputBuilder authenticationSettings(AuthenticationConfig authenticationConfig) {
+ this.authenticationConfig = authenticationConfig;
+ return this;
+ }
+
public ClusterInitCallInputBuilder clusterUrl(String clusterUrl) {
this.clusterUrl = clusterUrl;
return this;
@@ -110,14 +129,31 @@ public class ClusterInitCallInput implements CallInput {
* @return this builder
*/
public ClusterInitCallInputBuilder fromClusterInitOptions(ClusterInitOptions clusterInitOptions) {
- this.metaStorageNodes = clusterInitOptions.getMetaStorageNodes();
- this.cmgNodes = clusterInitOptions.getCmgNodes();
- this.clusterName = clusterInitOptions.getClusterName();
+ this.metaStorageNodes = clusterInitOptions.metaStorageNodes();
+ this.cmgNodes = clusterInitOptions.cmgNodes();
+ this.clusterName = clusterInitOptions.clusterName();
+ this.authenticationConfig = toAuthenticationConfig(clusterInitOptions.authenticationOptions());
return this;
}
public ClusterInitCallInput build() {
- return new ClusterInitCallInput(clusterUrl, metaStorageNodes, cmgNodes, clusterName);
+ return new ClusterInitCallInput(clusterUrl, metaStorageNodes, cmgNodes, clusterName, authenticationConfig);
+ }
+
+ private AuthenticationConfig toAuthenticationConfig(AuthenticationOptions options) {
+ if (options == null) {
+ return null;
+ }
+
+ return new AuthenticationConfig(options.enabled(), extractAuthenticationProviders(options));
+ }
+
+ private static List<AuthenticationProviderConfig> extractAuthenticationProviders(AuthenticationOptions options) {
+ if (!nullOrBlank(options.basicLogin()) && !nullOrBlank(options.basicPassword())) {
+ return List.of(new BasicAuthenticationProviderConfig("basic", options.basicLogin(), options.basicPassword()));
+ } else {
+ return List.of();
+ }
}
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/status/ClusterStatusCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/status/ClusterStatusCall.java
index bb726633f4..f052bb156c 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/status/ClusterStatusCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/status/ClusterStatusCall.java
@@ -20,12 +20,12 @@ package org.apache.ignite.internal.cli.call.cluster.status;
import jakarta.inject.Singleton;
import org.apache.ignite.internal.cli.call.cluster.status.ClusterStatus.ClusterStatusBuilder;
import org.apache.ignite.internal.cli.call.cluster.topology.PhysicalTopologyCall;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
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;
import org.apache.ignite.internal.cli.core.call.UrlCallInput;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.ClusterManagementApi;
import org.apache.ignite.rest.client.invoker.ApiException;
import org.apache.ignite.rest.client.model.ClusterState;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/LogicalTopologyCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/LogicalTopologyCall.java
index 8f721d3c54..540566c8b4 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/LogicalTopologyCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/LogicalTopologyCall.java
@@ -19,12 +19,12 @@ package org.apache.ignite.internal.cli.call.cluster.topology;
import jakarta.inject.Singleton;
import java.util.List;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
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;
import org.apache.ignite.internal.cli.core.call.UrlCallInput;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.TopologyApi;
import org.apache.ignite.rest.client.invoker.ApiException;
import org.apache.ignite.rest.client.model.ClusterNode;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/PhysicalTopologyCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/PhysicalTopologyCall.java
index 9036a6e320..c223cfcb22 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/PhysicalTopologyCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/PhysicalTopologyCall.java
@@ -19,12 +19,12 @@ package org.apache.ignite.internal.cli.call.cluster.topology;
import jakarta.inject.Singleton;
import java.util.List;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
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;
import org.apache.ignite.internal.cli.core.call.UrlCallInput;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.TopologyApi;
import org.apache.ignite.rest.client.invoker.ApiException;
import org.apache.ignite.rest.client.model.ClusterNode;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigShowCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigShowCall.java
index ebf2060d1c..c419e9ba12 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigShowCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigShowCall.java
@@ -18,10 +18,10 @@
package org.apache.ignite.internal.cli.call.configuration;
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
import org.apache.ignite.internal.cli.core.call.Call;
import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.ClusterConfigurationApi;
import org.apache.ignite.rest.client.invoker.ApiException;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigUpdateCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigUpdateCall.java
index 8e1f8ddd6d..791f7e988f 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigUpdateCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigUpdateCall.java
@@ -18,10 +18,10 @@
package org.apache.ignite.internal.cli.call.configuration;
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
import org.apache.ignite.internal.cli.core.call.Call;
import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.ClusterConfigurationApi;
import org.apache.ignite.rest.client.invoker.ApiException;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigShowCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigShowCall.java
index 456b323a1b..0fc6a6acd4 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigShowCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigShowCall.java
@@ -18,10 +18,10 @@
package org.apache.ignite.internal.cli.call.configuration;
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
import org.apache.ignite.internal.cli.core.call.Call;
import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.NodeConfigurationApi;
import org.apache.ignite.rest.client.invoker.ApiException;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigUpdateCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigUpdateCall.java
index c5dda6f002..c641eb665a 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigUpdateCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigUpdateCall.java
@@ -18,10 +18,10 @@
package org.apache.ignite.internal.cli.call.configuration;
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
import org.apache.ignite.internal.cli.core.call.Call;
import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.NodeConfigurationApi;
import org.apache.ignite.rest.client.invoker.ApiException;
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 f545f2f08d..2cd815fff9 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
@@ -23,7 +23,6 @@ import java.net.MalformedURLException;
import java.util.Objects;
import org.apache.ignite.internal.cli.config.CliConfigKeys;
import org.apache.ignite.internal.cli.config.StateConfigProvider;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
import org.apache.ignite.internal.cli.core.JdbcUrl;
import org.apache.ignite.internal.cli.core.call.Call;
import org.apache.ignite.internal.cli.core.call.CallOutput;
@@ -32,6 +31,7 @@ import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
import org.apache.ignite.internal.cli.core.repl.Session;
import org.apache.ignite.internal.cli.core.repl.SessionInfo;
import org.apache.ignite.internal.cli.core.repl.config.RootConfig;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.internal.cli.core.style.component.MessageUiComponent;
import org.apache.ignite.internal.cli.core.style.element.UiElements;
import org.apache.ignite.rest.client.api.NodeConfigurationApi;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSetListCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSetListCall.java
index d7bb97915b..65117353b0 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSetListCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSetListCall.java
@@ -19,12 +19,12 @@ package org.apache.ignite.internal.cli.call.node.metric;
import jakarta.inject.Singleton;
import java.util.List;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
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;
import org.apache.ignite.internal.cli.core.call.UrlCallInput;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.NodeMetricApi;
import org.apache.ignite.rest.client.invoker.ApiException;
import org.apache.ignite.rest.client.model.MetricSet;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceEnableCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceEnableCall.java
index 9f3576668e..dc02d734fd 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceEnableCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceEnableCall.java
@@ -18,11 +18,11 @@
package org.apache.ignite.internal.cli.call.node.metric;
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
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;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.NodeMetricApi;
import org.apache.ignite.rest.client.invoker.ApiException;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceListCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceListCall.java
index da036db076..78dabd5b27 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceListCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceListCall.java
@@ -19,12 +19,12 @@ package org.apache.ignite.internal.cli.call.node.metric;
import jakarta.inject.Singleton;
import java.util.List;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
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;
import org.apache.ignite.internal.cli.core.call.UrlCallInput;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.NodeMetricApi;
import org.apache.ignite.rest.client.invoker.ApiException;
import org.apache.ignite.rest.client.model.MetricSource;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/status/NodeStatusCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/status/NodeStatusCall.java
index 36707a7e4c..a27e483d27 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/status/NodeStatusCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/status/NodeStatusCall.java
@@ -18,12 +18,12 @@
package org.apache.ignite.internal.cli.call.node.status;
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
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;
import org.apache.ignite.internal.cli.core.call.UrlCallInput;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.NodeManagementApi;
import org.apache.ignite.rest.client.invoker.ApiException;
import org.apache.ignite.rest.client.model.NodeState;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/version/NodeVersionCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/version/NodeVersionCall.java
index ca1bbd28f3..60b36f01bb 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/version/NodeVersionCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/version/NodeVersionCall.java
@@ -18,12 +18,12 @@
package org.apache.ignite.internal.cli.call.node.version;
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
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;
import org.apache.ignite.internal.cli.core.call.UrlCallInput;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.NodeManagementApi;
import org.apache.ignite.rest.client.invoker.ApiException;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCall.java
index 7093accc12..8d8a94547a 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCall.java
@@ -23,7 +23,6 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.util.concurrent.CompletableFuture;
import okhttp3.Call;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
import org.apache.ignite.internal.cli.core.call.AsyncCall;
import org.apache.ignite.internal.cli.core.call.CallOutput;
import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
@@ -31,6 +30,7 @@ import org.apache.ignite.internal.cli.core.call.ProgressTracker;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
import org.apache.ignite.internal.cli.core.exception.UnitAlreadyExistsException;
import org.apache.ignite.internal.cli.core.repl.registry.UnitsRegistry;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.internal.cli.core.style.component.MessageUiComponent;
import org.apache.ignite.internal.cli.core.style.element.UiElements;
import org.apache.ignite.rest.client.api.DeploymentApi;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCallFactory.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCallFactory.java
index e5637eeb54..d756140dd8 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCallFactory.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCallFactory.java
@@ -18,9 +18,9 @@
package org.apache.ignite.internal.cli.call.unit;
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
import org.apache.ignite.internal.cli.core.call.ProgressTracker;
import org.apache.ignite.internal.cli.core.repl.registry.UnitsRegistry;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
/** Factory for {@link DeployUnitCall}. */
@Singleton
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/ListUnitCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/ListUnitCall.java
index 8a51311660..d62baca364 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/ListUnitCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/ListUnitCall.java
@@ -20,12 +20,12 @@ package org.apache.ignite.internal.cli.call.unit;
import jakarta.inject.Singleton;
import java.util.List;
import java.util.stream.Collectors;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
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;
import org.apache.ignite.internal.cli.core.call.UrlCallInput;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.DeploymentApi;
import org.apache.ignite.rest.client.invoker.ApiException;
import org.apache.ignite.rest.client.model.UnitStatus;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UndeployUnitCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UndeployUnitCall.java
index e6a532caf0..e3b536fa11 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UndeployUnitCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UndeployUnitCall.java
@@ -18,13 +18,13 @@
package org.apache.ignite.internal.cli.call.unit;
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
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;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
import org.apache.ignite.internal.cli.core.exception.UnitNotFoundException;
import org.apache.ignite.internal.cli.core.repl.registry.UnitsRegistry;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.internal.cli.core.style.component.MessageUiComponent;
import org.apache.ignite.internal.cli.core.style.element.UiElements;
import org.apache.ignite.rest.client.api.DeploymentApi;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusCall.java
index 64cc308eca..be3297b5d0 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusCall.java
@@ -18,12 +18,12 @@
package org.apache.ignite.internal.cli.call.unit;
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
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;
import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
import org.apache.ignite.internal.cli.core.exception.UnitNotFoundException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.rest.client.api.DeploymentApi;
import org.apache.ignite.rest.client.invoker.ApiException;
import org.apache.ignite.rest.client.model.UnitStatus;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
index 160805fb81..96c41bc7f7 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
@@ -53,7 +53,22 @@ public enum Options {
PLAIN(Constants.PLAIN_OPTION, Constants.PLAIN_OPTION, Constants.PLAIN_OPTION_DESC),
VERBOSE(Constants.VERBOSE_OPTION, Constants.VERBOSE_OPTION_SHORT, Constants.VERBOSE_OPTION_DESC),
HELP(Constants.HELP_OPTION, Constants.HELP_OPTION_SHORT, Constants.HELP_OPTION_DESC),
- VERSION(Constants.VERSION_OPTION, Constants.VERSION_OPTION, Constants.VERSION_OPTION_DESC);
+ VERSION(Constants.VERSION_OPTION, Constants.VERSION_OPTION, Constants.VERSION_OPTION_DESC),
+ AUTHENTICATION_ENABLED(
+ Constants.AUTHENTICATION_ENABLED_OPTION,
+ Constants.AUTHENTICATION_ENABLED_OPTION_SHORT,
+ Constants.AUTHENTICATION_ENABLED_OPTION_DESC
+ ),
+ BASIC_AUTHENTICATION_LOGIN(
+ Constants.BASIC_AUTHENTICATION_LOGIN_OPTION,
+ Constants.BASIC_AUTHENTICATION_LOGIN_OPTION_SHORT,
+ Constants.BASIC_AUTHENTICATION_LOGIN_OPTION_DESC
+ ),
+ BASIC_AUTHENTICATION_PASSWORD(
+ Constants.BASIC_AUTHENTICATION_PASSWORD_OPTION,
+ Constants.BASIC_AUTHENTICATION_PASSWORD_OPTION_SHORT,
+ Constants.BASIC_AUTHENTICATION_PASSWORD_OPTION_DESC
+ );
private final String fullName;
private final String shortName;
@@ -212,6 +227,9 @@ public enum Options {
/** Version option long name. */
public static final String VERSION_OPTION = "--version";
+ /** Version option description. */
+ public static final String VERSION_OPTION_DESC = "Print version information and exit";
+
/** Version option short name. */
public static final String UNIT_VERSION_OPTION_SHORT = "-uv";
@@ -227,7 +245,22 @@ public enum Options {
/** Unit path option description. */
public static final String UNIT_PATH_OPTION_DESC = "Path to deployment unit file or directory";
- /** Version option description. */
- public static final String VERSION_OPTION_DESC = "Print version information and exit";
+ public static final String AUTHENTICATION_ENABLED_OPTION = "--auth-enabled";
+
+ public static final String AUTHENTICATION_ENABLED_OPTION_SHORT = "-ae";
+
+ public static final String AUTHENTICATION_ENABLED_OPTION_DESC = "Authentication enabled flag";
+
+ public static final String BASIC_AUTHENTICATION_LOGIN_OPTION = "--basic-auth-login";
+
+ public static final String BASIC_AUTHENTICATION_LOGIN_OPTION_SHORT = "-bl";
+
+ public static final String BASIC_AUTHENTICATION_LOGIN_OPTION_DESC = "Login which will be used for basic authentication";
+
+ public static final String BASIC_AUTHENTICATION_PASSWORD_OPTION = "--basic-auth-password";
+
+ public static final String BASIC_AUTHENTICATION_PASSWORD_OPTION_SHORT = "-bp";
+
+ public static final String BASIC_AUTHENTICATION_PASSWORD_OPTION_DESC = "Password which will be used for basic authentication";
}
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/AuthenticationOptions.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/AuthenticationOptions.java
new file mode 100644
index 0000000000..668473f50e
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/AuthenticationOptions.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.cli.commands.cluster.init;
+
+import static org.apache.ignite.internal.cli.commands.Options.Constants.AUTHENTICATION_ENABLED_OPTION;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.AUTHENTICATION_ENABLED_OPTION_DESC;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.AUTHENTICATION_ENABLED_OPTION_SHORT;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.BASIC_AUTHENTICATION_LOGIN_OPTION;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.BASIC_AUTHENTICATION_LOGIN_OPTION_DESC;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.BASIC_AUTHENTICATION_LOGIN_OPTION_SHORT;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.BASIC_AUTHENTICATION_PASSWORD_OPTION;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.BASIC_AUTHENTICATION_PASSWORD_OPTION_DESC;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.BASIC_AUTHENTICATION_PASSWORD_OPTION_SHORT;
+
+import picocli.CommandLine.Option;
+
+/**
+ * Mixin class for authentication options.
+ */
+public class AuthenticationOptions {
+
+ @Option(names = {AUTHENTICATION_ENABLED_OPTION, AUTHENTICATION_ENABLED_OPTION_SHORT},
+ description = AUTHENTICATION_ENABLED_OPTION_DESC)
+ private boolean enabled;
+
+ @Option(names = {BASIC_AUTHENTICATION_LOGIN_OPTION, BASIC_AUTHENTICATION_LOGIN_OPTION_SHORT},
+ description = BASIC_AUTHENTICATION_LOGIN_OPTION_DESC)
+ private String basicLogin;
+
+ @Option(names = {BASIC_AUTHENTICATION_PASSWORD_OPTION, BASIC_AUTHENTICATION_PASSWORD_OPTION_SHORT},
+ description = BASIC_AUTHENTICATION_PASSWORD_OPTION_DESC)
+ private String basicPassword;
+
+ public boolean enabled() {
+ return enabled;
+ }
+
+ public String basicLogin() {
+ return basicLogin;
+ }
+
+ public String basicPassword() {
+ return basicPassword;
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
index 75fd02f512..cc340afb16 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
@@ -29,6 +29,7 @@ import static org.apache.ignite.internal.cli.commands.Options.Constants.META_STO
import java.util.ArrayList;
import java.util.List;
+import picocli.CommandLine.Mixin;
import picocli.CommandLine.Option;
/**
@@ -54,15 +55,22 @@ public class ClusterInitOptions {
@Option(names = {CLUSTER_NAME_OPTION, CLUSTER_NAME_OPTION_SHORT}, required = true, description = CLUSTER_NAME_OPTION_DESC)
private String clusterName;
- public List<String> getMetaStorageNodes() {
+ @Mixin
+ private AuthenticationOptions authenticationOptions;
+
+ public List<String> metaStorageNodes() {
return metaStorageNodes;
}
- public List<String> getCmgNodes() {
+ public List<String> cmgNodes() {
return cmgNodes;
}
- public String getClusterName() {
+ public String clusterName() {
return clusterName;
}
+
+ public AuthenticationOptions authenticationOptions() {
+ return authenticationOptions;
+ }
}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
index 15383b5472..1880450027 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
@@ -42,7 +42,13 @@ public enum CliConfigKeys {
REST_KEY_STORE_PASSWORD(Constants.REST_KEY_STORE_PASSWORD),
/** Default JDBC URL property name. */
- JDBC_URL(Constants.JDBC_URL);
+ JDBC_URL(Constants.JDBC_URL),
+
+ /** Basic authentication login. */
+ BASIC_AUTHENTICATION_LOGIN(Constants.BASIC_AUTHENTICATION_LOGIN),
+
+ /** Basic authentication password. */
+ BASIC_AUTHENTICATION_PASSWORD(Constants.BASIC_AUTHENTICATION_PASSWORD);
private final String value;
@@ -66,6 +72,10 @@ public enum CliConfigKeys {
public static final String JDBC_URL = "ignite.jdbc-url";
+ public static final String BASIC_AUTHENTICATION_LOGIN = "ignite.auth.basic.login";
+
+ public static final String BASIC_AUTHENTICATION_PASSWORD = "ignite.auth.basic.password";
+
/** Environment variable which points to the base directory for configuration files according to XDG spec. */
private static final String XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
index a509a75c96..29719649c5 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
@@ -17,9 +17,15 @@
package org.apache.ignite.internal.cli.config.ini;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_LOGIN;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_PASSWORD;
import static org.apache.ignite.internal.cli.config.CliConfigKeys.CLUSTER_URL;
import static org.apache.ignite.internal.cli.config.CliConfigKeys.Constants.CURRENT_PROFILE;
import static org.apache.ignite.internal.cli.config.CliConfigKeys.JDBC_URL;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PASSWORD;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PATH;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PASSWORD;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PATH;
import java.io.File;
import java.io.IOException;
@@ -119,6 +125,12 @@ public class IniConfigManager implements ConfigManager {
IniSection defaultSection = ini.createSection(DEFAULT_PROFILE_NAME);
defaultSection.setProperty(CLUSTER_URL.value(), "http://localhost:10300");
defaultSection.setProperty(JDBC_URL.value(), "jdbc:ignite:thin://127.0.0.1:10800");
+ defaultSection.setProperty(REST_KEY_STORE_PATH.value(), "");
+ defaultSection.setProperty(REST_KEY_STORE_PASSWORD.value(), "");
+ defaultSection.setProperty(REST_TRUST_STORE_PATH.value(), "");
+ defaultSection.setProperty(REST_TRUST_STORE_PASSWORD.value(), "");
+ defaultSection.setProperty(BASIC_AUTHENTICATION_LOGIN.value(), "");
+ defaultSection.setProperty(BASIC_AUTHENTICATION_PASSWORD.value(), "");
ini.store();
return ini;
} catch (IOException e) {
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientBuilder.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientBuilder.java
deleted file mode 100644
index 287a075424..0000000000
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientBuilder.java
+++ /dev/null
@@ -1,145 +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;
-
-import static org.apache.ignite.internal.util.StringUtils.nullOrBlank;
-
-import java.io.File;
-import java.io.IOException;
-import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.CertificateException;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
-import okhttp3.OkHttpClient;
-import okhttp3.OkHttpClient.Builder;
-import okhttp3.internal.tls.OkHostnameVerifier;
-import org.apache.ignite.rest.client.invoker.ApiClient;
-
-class ApiClientBuilder {
- private String basePath;
- private String keyStorePath;
- private String keyStorePassword;
- private String trustStorePath;
- private String trustStorePassword;
-
- static ApiClientBuilder create() {
- return new ApiClientBuilder();
- }
-
- ApiClientBuilder basePath(String basePath) {
- this.basePath = basePath;
- return this;
- }
-
- ApiClientBuilder keyStorePath(String keyStorePath) {
- this.keyStorePath = keyStorePath;
- return this;
- }
-
- ApiClientBuilder keyStorePassword(String keyStorePassword) {
- this.keyStorePassword = keyStorePassword;
- return this;
- }
-
- ApiClientBuilder trustStorePath(String trustStorePath) {
- this.trustStorePath = trustStorePath;
- return this;
- }
-
- ApiClientBuilder trustStorePassword(String trustStorePassword) {
- this.trustStorePassword = trustStorePassword;
- return this;
- }
-
- ApiClient build() throws CertificateException,
- NoSuchAlgorithmException,
- KeyStoreException,
- IOException,
- UnrecoverableKeyException,
- KeyManagementException {
-
- Builder builder = new Builder();
-
- if (!nullOrBlank(keyStorePath) || !nullOrBlank(trustStorePath)) {
- applySslSettings(builder);
- }
-
- OkHttpClient okHttpClient = builder.build();
-
- return new ApiClient(okHttpClient)
- .setBasePath(basePath);
- }
-
- private Builder applySslSettings(Builder builder) throws UnrecoverableKeyException,
- CertificateException,
- NoSuchAlgorithmException,
- KeyStoreException,
- IOException,
- KeyManagementException {
-
- TrustManagerFactory trustManagerFactory = trustManagerFactory();
- KeyManagerFactory keyManagerFactory = keyManagerFactory();
-
- TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
- KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
-
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(keyManagers, trustManagers, new SecureRandom());
- return builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0])
- .hostnameVerifier(OkHostnameVerifier.INSTANCE);
- }
-
- private KeyManagerFactory keyManagerFactory()
- throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
- KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
-
- if (nullOrBlank(keyStorePath)) {
- keyManagerFactory.init(null, null);
- } else {
- char[] password = keyStorePassword == null ? null : keyStorePassword.toCharArray();
- KeyStore keyStore = KeyStore.getInstance(new File(keyStorePath), password);
- keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
- }
-
- return keyManagerFactory;
- }
-
- private TrustManagerFactory trustManagerFactory()
- throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
- TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
-
- if (nullOrBlank(trustStorePath)) {
- trustManagerFactory.init((KeyStore) null);
- } else {
- char[] password = trustStorePassword == null ? null : trustStorePassword.toCharArray();
- KeyStore trustStore = KeyStore.getInstance(new File(trustStorePath), password);
- trustManagerFactory.init(trustStore);
- }
-
- return trustManagerFactory;
- }
-}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientFactory.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientFactory.java
deleted file mode 100644
index 1960f97ba8..0000000000
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientFactory.java
+++ /dev/null
@@ -1,70 +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;
-
-import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PASSWORD;
-import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PATH;
-import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PASSWORD;
-import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PATH;
-
-import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.config.ConfigManager;
-import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
-import org.apache.ignite.internal.cli.core.exception.IgniteCliException;
-import org.apache.ignite.internal.cli.logger.CliLoggers;
-import org.apache.ignite.rest.client.invoker.ApiClient;
-
-/**
- * This class holds the map from URLs to {@link ApiClient}.
- */
-@Singleton
-public class ApiClientFactory {
-
- private final ConfigManagerProvider configManagerProvider;
-
- public ApiClientFactory(ConfigManagerProvider configManagerProvider) {
- this.configManagerProvider = configManagerProvider;
- }
-
- /**
- * Gets {@link ApiClient} for the base path.
- *
- * @param path Base path.
- * @return created API client.
- */
- public ApiClient getClient(String path) {
- ApiClient apiClient = createClient(path);
- CliLoggers.addApiClient(path, apiClient);
- return apiClient;
- }
-
- private ApiClient createClient(String path) {
- try {
- ConfigManager configManager = configManagerProvider.get();
- return ApiClientBuilder.create()
- .basePath(path)
- .keyStorePath(configManager.getCurrentProperty(REST_KEY_STORE_PATH.value()))
- .keyStorePassword(configManager.getCurrentProperty(REST_KEY_STORE_PASSWORD.value()))
- .trustStorePath(configManager.getCurrentProperty(REST_TRUST_STORE_PATH.value()))
- .trustStorePassword(configManager.getCurrentProperty(REST_TRUST_STORE_PASSWORD.value()))
- .build();
- } catch (Exception e) {
- throw new IgniteCliException("Couldn't build REST client", e);
- }
- }
-}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
index a3f99a0098..6ae12c042c 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
@@ -69,7 +69,16 @@ public class IgniteCliApiExceptionHandler implements ExceptionHandler<IgniteCliA
} else if (apiCause != null) {
errorComponentBuilder.header(apiCause.getMessage());
} else {
- tryToExtractProblem(errorComponentBuilder, cause);
+ Problem problem = extractProblem(cause);
+ if (problem.getStatus() == 401) {
+ errorComponentBuilder
+ .header("Authentication error")
+ .details("Could not connect to node with URL %s. "
+ + "Check authentication configuration", UiElements.url(e.getUrl()))
+ .verbose(e.getMessage());
+ } else {
+ renderProblem(errorComponentBuilder, problem);
+ }
}
} else {
errorComponentBuilder.header(e.getCause() != e ? e.getCause().getMessage() : e.getMessage());
@@ -84,22 +93,25 @@ public class IgniteCliApiExceptionHandler implements ExceptionHandler<IgniteCliA
return 1;
}
- private static void tryToExtractProblem(ErrorComponentBuilder errorComponentBuilder, ApiException cause) {
+ private static Problem extractProblem(ApiException cause) {
try {
- Problem problem = objectMapper.readValue(cause.getResponseBody(), Problem.class);
- List<InvalidParam> invalidParams = problem.getInvalidParams();
- if (invalidParams != null && !invalidParams.isEmpty()) {
- errorComponentBuilder.details(extractInvalidParams(invalidParams));
- }
- errorComponentBuilder
- .header(problem.getDetail() != null ? problem.getDetail() : problem.getTitle())
- .errorCode(problem.getCode())
- .traceId(problem.getTraceId());
+ return objectMapper.readValue(cause.getResponseBody(), Problem.class);
} catch (JsonProcessingException ex) {
throw new RuntimeException(ex);
}
}
+ private static void renderProblem(ErrorComponentBuilder errorComponentBuilder, Problem problem) {
+ List<InvalidParam> invalidParams = problem.getInvalidParams();
+ if (invalidParams != null && !invalidParams.isEmpty()) {
+ errorComponentBuilder.details(extractInvalidParams(invalidParams));
+ }
+ errorComponentBuilder
+ .header(problem.getDetail() != null ? problem.getDetail() : problem.getTitle())
+ .errorCode(problem.getCode())
+ .traceId(problem.getTraceId());
+ }
+
@NotNull
private static String extractInvalidParams(List<InvalidParam> invalidParams) {
return invalidParams.stream()
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/registry/impl/JdbcUrlRegistryImpl.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/registry/impl/JdbcUrlRegistryImpl.java
index 772b4e29d0..21a6b15345 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/registry/impl/JdbcUrlRegistryImpl.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/registry/impl/JdbcUrlRegistryImpl.java
@@ -29,13 +29,13 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
import org.apache.ignite.internal.cli.core.JdbcUrl;
import org.apache.ignite.internal.cli.core.repl.AsyncSessionEventListener;
import org.apache.ignite.internal.cli.core.repl.SessionInfo;
import org.apache.ignite.internal.cli.core.repl.config.RootConfig;
import org.apache.ignite.internal.cli.core.repl.registry.JdbcUrlRegistry;
import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
import org.apache.ignite.internal.cli.logger.CliLoggers;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.thread.NamedThreadFactory;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientFactory.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientFactory.java
new file mode 100644
index 0000000000..0eaff4c84d
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientFactory.java
@@ -0,0 +1,178 @@
+/*
+ * 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.rest;
+
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_LOGIN;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_PASSWORD;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PASSWORD;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PATH;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PASSWORD;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PATH;
+import static org.apache.ignite.internal.util.StringUtils.nullOrBlank;
+
+import jakarta.inject.Singleton;
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import okhttp3.Interceptor;
+import okhttp3.OkHttpClient;
+import okhttp3.OkHttpClient.Builder;
+import okhttp3.internal.tls.OkHostnameVerifier;
+import org.apache.ignite.internal.cli.config.ConfigManager;
+import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
+import org.apache.ignite.internal.cli.core.exception.IgniteCliException;
+import org.apache.ignite.internal.cli.logger.CliLoggers;
+import org.apache.ignite.rest.client.invoker.ApiClient;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Factory for {@link ApiClient}. This class holds the map from {@link ApiClientSettings} to {@link ApiClient}. If settings are changed, the
+ * factory will recreate a new client. Otherwise, returns a client from the cache.
+ */
+@Singleton
+public class ApiClientFactory {
+
+ private final Map<ApiClientSettings, ApiClient> clientMap = new ConcurrentHashMap<>();
+
+ private final ConfigManagerProvider configManagerProvider;
+
+ public ApiClientFactory(ConfigManagerProvider configManagerProvider) {
+ this.configManagerProvider = configManagerProvider;
+ }
+
+ /**
+ * Returns {@link ApiClient} for the base path.
+ *
+ * @param path Base path.
+ * @return created API client.
+ */
+ public ApiClient getClient(String path) {
+ ApiClient apiClient = clientMap.computeIfAbsent(settings(path), this::buildClient);
+ CliLoggers.addApiClient(path, apiClient);
+ return apiClient;
+ }
+
+ private ApiClientSettings settings(String path) {
+ ConfigManager configManager = configManagerProvider.get();
+ return ApiClientSettings.builder()
+ .basePath(path)
+ .keyStorePath(configManager.getCurrentProperty(REST_KEY_STORE_PATH.value()))
+ .keyStorePassword(configManager.getCurrentProperty(REST_KEY_STORE_PASSWORD.value()))
+ .trustStorePath(configManager.getCurrentProperty(REST_TRUST_STORE_PATH.value()))
+ .trustStorePassword(configManager.getCurrentProperty(REST_TRUST_STORE_PASSWORD.value()))
+ .basicAuthLogin(configManager.getCurrentProperty(BASIC_AUTHENTICATION_LOGIN.value()))
+ .basicAuthPassword(configManager.getCurrentProperty(BASIC_AUTHENTICATION_PASSWORD.value()))
+ .build();
+ }
+
+
+ private ApiClient buildClient(ApiClientSettings settings) {
+ try {
+ Builder builder = new Builder();
+
+ if (!nullOrBlank(settings.keyStorePath()) || !nullOrBlank(settings.keyStorePassword())) {
+ applySslSettings(builder, settings);
+ }
+
+ Interceptor authInterceptor = authInterceptor(settings);
+ if (authInterceptor != null) {
+ builder.addInterceptor(authInterceptor);
+ }
+
+ OkHttpClient okHttpClient = builder.build();
+
+ return new ApiClient(okHttpClient)
+ .setBasePath(settings.basePath());
+
+ } catch (Exception e) {
+ throw new IgniteCliException("Couldn't build REST client", e);
+ }
+ }
+
+ private Builder applySslSettings(Builder builder, ApiClientSettings settings) throws UnrecoverableKeyException,
+ CertificateException,
+ NoSuchAlgorithmException,
+ KeyStoreException,
+ IOException,
+ KeyManagementException {
+
+ TrustManagerFactory trustManagerFactory = trustManagerFactory(settings);
+ KeyManagerFactory keyManagerFactory = keyManagerFactory(settings);
+
+ TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+ KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
+
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(keyManagers, trustManagers, new SecureRandom());
+ return builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0])
+ .hostnameVerifier(OkHostnameVerifier.INSTANCE);
+ }
+
+ private KeyManagerFactory keyManagerFactory(ApiClientSettings settings)
+ throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
+ KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+
+ if (nullOrBlank(settings.keyStorePath())) {
+ keyManagerFactory.init(null, null);
+ } else {
+ char[] password = settings.keyStorePassword() == null ? null : settings.keyStorePassword().toCharArray();
+ KeyStore keyStore = KeyStore.getInstance(new File(settings.keyStorePath()), password);
+ keyManagerFactory.init(keyStore, settings.keyStorePassword().toCharArray());
+ }
+
+ return keyManagerFactory;
+ }
+
+ private TrustManagerFactory trustManagerFactory(ApiClientSettings settings)
+ throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+
+ if (nullOrBlank(settings.trustStorePath())) {
+ trustManagerFactory.init((KeyStore) null);
+ } else {
+ char[] password = settings.trustStorePassword() == null ? null : settings.trustStorePassword().toCharArray();
+ KeyStore trustStore = KeyStore.getInstance(new File(settings.trustStorePath()), password);
+ trustManagerFactory.init(trustStore);
+ }
+
+ return trustManagerFactory;
+ }
+
+ @Nullable
+ private Interceptor authInterceptor(ApiClientSettings settings) {
+ if (!nullOrBlank(settings.basicAuthLogin()) && !nullOrBlank(settings.basicAuthPassword())) {
+ return new BasicAuthenticationInterceptor(settings.basicAuthLogin(), settings.basicAuthPassword());
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientSettings.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientSettings.java
new file mode 100644
index 0000000000..091baf6e96
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientSettings.java
@@ -0,0 +1,103 @@
+/*
+ * 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.rest;
+
+import java.util.Objects;
+
+/** Api client settings. */
+public class ApiClientSettings {
+
+ private String basePath;
+
+ private String keyStorePath;
+
+ private String keyStorePassword;
+
+ private String trustStorePath;
+
+ private String trustStorePassword;
+
+ private String basicAuthLogin;
+
+ private String basicAuthPassword;
+
+ ApiClientSettings(String basePath, String keyStorePath, String keyStorePassword, String trustStorePath,
+ String trustStorePassword,
+ String basicAuthLogin, String basicAuthPassword) {
+ this.basePath = basePath;
+ this.keyStorePath = keyStorePath;
+ this.keyStorePassword = keyStorePassword;
+ this.trustStorePath = trustStorePath;
+ this.trustStorePassword = trustStorePassword;
+ this.basicAuthLogin = basicAuthLogin;
+ this.basicAuthPassword = basicAuthPassword;
+ }
+
+ public static ApiClientSettingsBuilder builder() {
+ return new ApiClientSettingsBuilder();
+ }
+
+ public String basePath() {
+ return basePath;
+ }
+
+ public String keyStorePath() {
+ return keyStorePath;
+ }
+
+ public String keyStorePassword() {
+ return keyStorePassword;
+ }
+
+ public String trustStorePath() {
+ return trustStorePath;
+ }
+
+ public String trustStorePassword() {
+ return trustStorePassword;
+ }
+
+ public String basicAuthLogin() {
+ return basicAuthLogin;
+ }
+
+ public String basicAuthPassword() {
+ return basicAuthPassword;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ApiClientSettings that = (ApiClientSettings) o;
+ return Objects.equals(basePath, that.basePath) && Objects.equals(keyStorePath, that.keyStorePath)
+ && Objects.equals(keyStorePassword, that.keyStorePassword) && Objects.equals(trustStorePath,
+ that.trustStorePath) && Objects.equals(trustStorePassword, that.trustStorePassword) && Objects.equals(
+ basicAuthLogin, that.basicAuthLogin) && Objects.equals(basicAuthPassword, that.basicAuthPassword);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(basePath, keyStorePath, keyStorePassword, trustStorePath, trustStorePassword, basicAuthLogin,
+ basicAuthPassword);
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientSettingsBuilder.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientSettingsBuilder.java
new file mode 100644
index 0000000000..6ba9b4b027
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientSettingsBuilder.java
@@ -0,0 +1,69 @@
+/*
+ * 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.rest;
+
+/** Builder for {@link ApiClientSettings}. */
+public class ApiClientSettingsBuilder {
+ private String basePath;
+ private String keyStorePath;
+ private String keyStorePassword;
+ private String trustStorePath;
+ private String trustStorePassword;
+ private String basicAuthLogin;
+ private String basicAuthPassword;
+
+ public ApiClientSettingsBuilder basePath(String basePath) {
+ this.basePath = basePath;
+ return this;
+ }
+
+ public ApiClientSettingsBuilder keyStorePath(String keyStorePath) {
+ this.keyStorePath = keyStorePath;
+ return this;
+ }
+
+ public ApiClientSettingsBuilder keyStorePassword(String keyStorePassword) {
+ this.keyStorePassword = keyStorePassword;
+ return this;
+ }
+
+ public ApiClientSettingsBuilder trustStorePath(String trustStorePath) {
+ this.trustStorePath = trustStorePath;
+ return this;
+ }
+
+ public ApiClientSettingsBuilder trustStorePassword(String trustStorePassword) {
+ this.trustStorePassword = trustStorePassword;
+ return this;
+ }
+
+ public ApiClientSettingsBuilder basicAuthLogin(String basicAuthLogin) {
+ this.basicAuthLogin = basicAuthLogin;
+ return this;
+ }
+
+ public ApiClientSettingsBuilder basicAuthPassword(String basicAuthPassword) {
+ this.basicAuthPassword = basicAuthPassword;
+ return this;
+ }
+
+ public ApiClientSettings build() {
+ return new ApiClientSettings(basePath, keyStorePath, keyStorePassword, trustStorePath, trustStorePassword, basicAuthLogin,
+ basicAuthPassword);
+ }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/BasicAuthenticationInterceptor.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/BasicAuthenticationInterceptor.java
new file mode 100644
index 0000000000..bbf7af037f
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/BasicAuthenticationInterceptor.java
@@ -0,0 +1,44 @@
+/*
+ * 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.rest;
+
+import java.io.IOException;
+import okhttp3.Credentials;
+import okhttp3.Interceptor;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * Basic authentication interceptor. Adds "Authorization" header with basic authentication.
+ */
+public class BasicAuthenticationInterceptor implements Interceptor {
+
+ private final String credentials;
+
+ public BasicAuthenticationInterceptor(String user, String password) {
+ this.credentials = Credentials.basic(user, password);
+ }
+
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ Request request = chain.request();
+ Request authenticatedRequest = request.newBuilder()
+ .header("Authorization", credentials).build();
+ return chain.proceed(authenticatedRequest);
+ }
+}
diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/IgniteCliInterfaceTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/IgniteCliInterfaceTest.java
index 75a406a25f..3093843e9b 100644
--- a/modules/cli/src/test/java/org/apache/ignite/internal/cli/IgniteCliInterfaceTest.java
+++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/IgniteCliInterfaceTest.java
@@ -271,6 +271,63 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
assertThatStderrIsEmpty();
}
+ @Test
+ @DisplayName("init --cluster-endpoint-url http://localhost:10300 --meta-storage-node node1ConsistentId --meta-storage-node node2ConsistentId "
+ + "--cmg-node node2ConsistentId --cmg-node node3ConsistentId --cluster-name cluster "
+ + "--auth-enabled --basic-auth-login admin --basic-auth-password password")
+ void initWithAuthenticationSuccess() {
+ var expectedSentContent = "{\n"
+ + " \"metaStorageNodes\": [\n"
+ + " \"node1ConsistentId\",\n"
+ + " \"node2ConsistentId\"\n"
+ + " ],\n"
+ + " \"cmgNodes\": [\n"
+ + " \"node2ConsistentId\",\n"
+ + " \"node3ConsistentId\"\n"
+ + " ],\n"
+ + " \"clusterName\": \"cluster\",\n"
+ + " \"authenticationConfig\": {\n"
+ + " \"enabled\": true,\n"
+ + " \"providers\": [\n"
+ + " {\n"
+ + " \"login\": \"admin\",\n"
+ + " \"password\": \"password\",\n"
+ + " \"name\": \"basic\",\n"
+ + " \"type\": \"BASIC\"\n"
+ + " }\n"
+ + " ]\n"
+ + " }\n"
+ + "}";
+
+ clientAndServer
+ .when(request()
+ .withMethod("POST")
+ .withPath("/management/v1/cluster/init")
+ .withBody(json(expectedSentContent, ONLY_MATCHING_FIELDS))
+ .withContentType(MediaType.APPLICATION_JSON_UTF_8)
+ )
+ .respond(response(null));
+
+
+ int exitCode = execute(
+ "cluster", "init",
+ "--cluster-endpoint-url", mockUrl,
+ "--meta-storage-node", "node1ConsistentId",
+ "--meta-storage-node", "node2ConsistentId",
+ "--cmg-node", "node2ConsistentId",
+ "--cmg-node", "node3ConsistentId",
+ "--cluster-name", "cluster",
+ "--auth-enabled",
+ "--basic-auth-login", "admin",
+ "--basic-auth-password", "password"
+ );
+
+ assertThatExitCodeMeansSuccess(exitCode);
+
+ assertOutputEqual("Cluster was initialized successfully");
+ assertThatStderrIsEmpty();
+ }
+
@Test
void initError() {
clientAndServer