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

isapego 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 59a1124f49 IGNITE-19101 Basic authentication for C++ client (#1953)
59a1124f49 is described below

commit 59a1124f4983e2fba9a19fd7cc90a79aab54405f
Author: Igor Sapego <[email protected]>
AuthorDate: Fri Apr 21 16:54:21 2023 +0400

    IGNITE-19101 Basic authentication for C++ client (#1953)
---
 modules/platforms/cpp/ignite/client/CMakeLists.txt |   2 +
 .../cpp/ignite/client/basic_authenticator.h        | 102 +++++++++++++
 .../ignite/client/detail/cluster_connection.cpp    |   2 +-
 .../cpp/ignite/client/detail/node_connection.cpp   |  43 ++++--
 .../cpp/ignite/client/detail/node_connection.h     |  15 +-
 .../platforms/cpp/ignite/client/ignite_client.cpp  |  17 ++-
 .../client/ignite_client_authenticator.h}          |  57 ++++---
 .../ignite/client/ignite_client_configuration.h    |  28 +++-
 modules/platforms/cpp/ignite/protocol/writer.h     |  14 ++
 .../platforms/cpp/tests/client-test/CMakeLists.txt |   1 +
 .../tests/client-test/basic_authenticator_test.cpp | 170 +++++++++++++++++++++
 .../cpp/tests/client-test/compute_test.cpp         |   4 -
 .../cpp/tests/client-test/ignite_client_test.cpp   |   3 +-
 .../cpp/tests/client-test/ignite_runner_suite.h    |   2 +
 modules/platforms/cpp/tests/client-test/main.cpp   |  11 +-
 15 files changed, 406 insertions(+), 65 deletions(-)

diff --git a/modules/platforms/cpp/ignite/client/CMakeLists.txt 
b/modules/platforms/cpp/ignite/client/CMakeLists.txt
index 614708bd0f..26c616cf88 100644
--- a/modules/platforms/cpp/ignite/client/CMakeLists.txt
+++ b/modules/platforms/cpp/ignite/client/CMakeLists.txt
@@ -41,7 +41,9 @@ set(SOURCES
 )
 
 set(PUBLIC_HEADERS
+    basic_authenticator.h
     ignite_client.h
+    ignite_client_authenticator.h
     ignite_client_configuration.h
     ignite_logger.h
     primitive.h
diff --git a/modules/platforms/cpp/ignite/client/basic_authenticator.h 
b/modules/platforms/cpp/ignite/client/basic_authenticator.h
new file mode 100644
index 0000000000..b16226ebc3
--- /dev/null
+++ b/modules/platforms/cpp/ignite/client/basic_authenticator.h
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "ignite/client/ignite_client_authenticator.h"
+
+#include <string>
+
+namespace ignite {
+
+/**
+ * Basic authenticator with username and password.
+ *
+ * Credentials are sent to the server in plain text, unless SSL/TLS is enabled.
+ */
+class basic_authenticator : public ignite_client_authenticator {
+public:
+    /** Type constant. */
+    inline static const std::string TYPE{"basic"};
+
+    // Default
+    basic_authenticator() = default;
+
+    /**
+     * Constructor.
+     *
+     * @param username Username.
+     * @param password Password.
+     */
+    basic_authenticator(std::string username, std::string password)
+        : m_username(std::move(username))
+        , m_password(std::move(password)) { }
+
+    /**
+     * Get authenticator type.
+     *
+     * @return Authenticator type.
+     */
+    [[nodiscard]] const std::string& get_type() const override {
+        return TYPE;
+    }
+
+    /**
+     * Get identity.
+     *
+     * @return Username.
+     */
+    [[nodiscard]] const std::string& get_identity() const override {
+        return m_username;
+    }
+
+    /**
+     * Set username.
+     *
+     * @param username Username.
+     */
+    void set_username(std::string username) {
+        m_username = std::move(username);
+    };
+
+    /**
+     * Get secret.
+     *
+     * @return Password.
+     */
+    [[nodiscard]] const std::string& get_secret() const override {
+        return m_password;
+    }
+
+    /**
+     * Set password.
+     *
+     * @param password Password.
+     */
+    void set_password(std::string password) {
+        m_password = std::move(password);
+    };
+
+private:
+    /** Username. */
+    std::string m_username;
+
+    /** Password. */
+    std::string m_password;
+};
+
+} // namespace ignite
diff --git a/modules/platforms/cpp/ignite/client/detail/cluster_connection.cpp 
b/modules/platforms/cpp/ignite/client/detail/cluster_connection.cpp
index 9c997907f4..330119afd4 100644
--- a/modules/platforms/cpp/ignite/client/detail/cluster_connection.cpp
+++ b/modules/platforms/cpp/ignite/client/detail/cluster_connection.cpp
@@ -76,7 +76,7 @@ void cluster_connection::on_connection_success(const 
network::end_point &addr, u
     m_logger->log_info("Established connection with remote host " + 
addr.to_string());
     m_logger->log_debug("Connection ID: " + std::to_string(id));
 
-    auto connection = node_connection::make_new(id, m_pool, m_logger);
+    auto connection = node_connection::make_new(id, m_pool, m_logger, 
m_configuration);
     {
         [[maybe_unused]] std::unique_lock<std::recursive_mutex> 
lock(m_connections_mutex);
 
diff --git a/modules/platforms/cpp/ignite/client/detail/node_connection.cpp 
b/modules/platforms/cpp/ignite/client/detail/node_connection.cpp
index 0b4d8ea745..b3d794e3b3 100644
--- a/modules/platforms/cpp/ignite/client/detail/node_connection.cpp
+++ b/modules/platforms/cpp/ignite/client/detail/node_connection.cpp
@@ -21,12 +21,12 @@
 
 namespace ignite::detail {
 
-node_connection::node_connection(
-    uint64_t id, std::shared_ptr<network::async_client_pool> pool, 
std::shared_ptr<ignite_logger> logger)
+node_connection::node_connection(uint64_t id, 
std::shared_ptr<network::async_client_pool> pool,
+    std::shared_ptr<ignite_logger> logger, const ignite_client_configuration 
&cfg)
     : m_id(id)
     , m_pool(std::move(pool))
-    , m_logger(std::move(logger)) {
-}
+    , m_logger(std::move(logger))
+    , m_configuration(cfg) { }
 
 node_connection::~node_connection() {
     for (auto &handler : m_request_handlers) {
@@ -44,26 +44,35 @@ node_connection::~node_connection() {
 bool node_connection::handshake() {
     static constexpr int8_t CLIENT_TYPE = 2;
 
+    std::map<std::string, std::string> extensions;
+    auto authenticator = m_configuration.get_authenticator();
+    if (authenticator) {
+        extensions.emplace("authn-type", authenticator->get_type());
+        extensions.emplace("authn-identity", authenticator->get_identity());
+        extensions.emplace("authn-secret", authenticator->get_secret());
+    }
+
     std::vector<std::byte> message;
     {
         protocol::buffer_adapter buffer(message);
         buffer.write_raw(bytes_view(protocol::MAGIC_BYTES));
 
-        protocol::write_message_to_buffer(buffer, [&context = 
m_protocol_context](protocol::writer &writer) {
-            auto ver = context.get_version();
+        protocol::write_message_to_buffer(buffer,
+            [&context = m_protocol_context, &extensions](protocol::writer 
&writer) {
+                auto ver = context.get_version();
 
-            writer.write(ver.major());
-            writer.write(ver.minor());
-            writer.write(ver.patch());
+                writer.write(ver.major());
+                writer.write(ver.minor());
+                writer.write(ver.patch());
 
-            writer.write(CLIENT_TYPE);
+                writer.write(CLIENT_TYPE);
 
-            // Features.
-            writer.write_binary_empty();
+                // Features.
+                writer.write_binary_empty();
 
-            // Extensions.
-            writer.write_map_empty();
-        });
+                // Extensions.
+                writer.write_map(extensions);
+            });
     }
 
     return m_pool->send(m_id, std::move(message));
@@ -121,8 +130,10 @@ ignite_result<void> 
node_connection::process_handshake_rsp(bytes_view msg) {
         return {ignite_error("Unsupported server version: " + 
ver.to_string())};
 
     auto err = protocol::read_error(reader);
-    if (err)
+    if (err) {
+        m_logger->log_warning("Handshake error: " + err.value().what_str());
         return {ignite_error(err.value())};
+    }
 
     (void) reader.read_int64(); // TODO: IGNITE-17606 Implement heartbeats
     (void) reader.read_string_nullable(); // Cluster node ID. Needed for 
partition-aware compute.
diff --git a/modules/platforms/cpp/ignite/client/detail/node_connection.h 
b/modules/platforms/cpp/ignite/client/detail/node_connection.h
index b474261334..c58044a2e9 100644
--- a/modules/platforms/cpp/ignite/client/detail/node_connection.h
+++ b/modules/platforms/cpp/ignite/client/detail/node_connection.h
@@ -64,11 +64,12 @@ public:
      * @param id Connection ID.
      * @param pool Connection pool.
      * @param logger Logger.
+     * @param cfg Configuration.
      * @return New instance.
      */
-    static std::shared_ptr<node_connection> make_new(
-        uint64_t id, std::shared_ptr<network::async_client_pool> pool, 
std::shared_ptr<ignite_logger> logger) {
-        return std::shared_ptr<node_connection>(new node_connection(id, 
std::move(pool), std::move(logger)));
+    static std::shared_ptr<node_connection> make_new(uint64_t id, 
std::shared_ptr<network::async_client_pool> pool,
+        std::shared_ptr<ignite_logger> logger, const 
ignite_client_configuration& cfg) {
+        return std::shared_ptr<node_connection>(new node_connection(id, 
std::move(pool), std::move(logger), cfg));
     }
 
     /**
@@ -196,9 +197,10 @@ private:
      * @param id Connection ID.
      * @param pool Connection pool.
      * @param logger Logger.
+     * @param cfg Configuration.
      */
-    node_connection(
-        uint64_t id, std::shared_ptr<network::async_client_pool> pool, 
std::shared_ptr<ignite_logger> logger);
+    node_connection(uint64_t id, std::shared_ptr<network::async_client_pool> 
pool,
+        std::shared_ptr<ignite_logger> logger, const 
ignite_client_configuration &cfg);
 
     /**
      * Generate next request ID.
@@ -238,6 +240,9 @@ private:
 
     /** Logger. */
     std::shared_ptr<ignite_logger> m_logger;
+
+    /** Configuration. */
+    const ignite_client_configuration& m_configuration;
 };
 
 } // namespace ignite::detail
diff --git a/modules/platforms/cpp/ignite/client/ignite_client.cpp 
b/modules/platforms/cpp/ignite/client/ignite_client.cpp
index 16743e5bd9..7b5224c4d2 100644
--- a/modules/platforms/cpp/ignite/client/ignite_client.cpp
+++ b/modules/platforms/cpp/ignite/client/ignite_client.cpp
@@ -38,16 +38,10 @@ void ignite_client::start_async(ignite_client_configuration 
configuration, std::
 ignite_client ignite_client::start(ignite_client_configuration configuration, 
std::chrono::milliseconds timeout) {
     auto impl = 
std::make_shared<detail::ignite_client_impl>(std::move(configuration));
 
-    auto promise = std::make_shared<std::promise<void>>();
+    auto promise = std::make_shared<std::promise<ignite_result<void>>>();
     auto future = promise->get_future();
 
-    impl->start([impl, promise](ignite_result<void> res) mutable {
-        if (!res) {
-            impl->stop();
-            promise->set_exception(std::make_exception_ptr(res.error()));
-        } else
-            promise->set_value();
-    });
+    impl->start(result_promise_setter(promise));
 
     auto status = future.wait_for(timeout);
     if (status == std::future_status::timeout) {
@@ -55,6 +49,13 @@ ignite_client 
ignite_client::start(ignite_client_configuration configuration, st
         throw ignite_error("Can not establish connection within timeout");
     }
 
+    assert(status == std::future_status::ready);
+    auto res = future.get();
+    if (res.has_error()) {
+        impl->stop();
+        throw ignite_error(res.error());
+    }
+
     return ignite_client(std::move(impl));
 }
 
diff --git a/modules/platforms/cpp/tests/client-test/ignite_client_test.cpp 
b/modules/platforms/cpp/ignite/client/ignite_client_authenticator.h
similarity index 51%
copy from modules/platforms/cpp/tests/client-test/ignite_client_test.cpp
copy to modules/platforms/cpp/ignite/client/ignite_client_authenticator.h
index 548dff30fa..f74fbaecf9 100644
--- a/modules/platforms/cpp/tests/client-test/ignite_client_test.cpp
+++ b/modules/platforms/cpp/ignite/client/ignite_client_authenticator.h
@@ -15,31 +15,42 @@
  * limitations under the License.
  */
 
-#include "ignite_runner_suite.h"
+#pragma once
 
-#include <ignite/client/ignite_client.h>
-#include <ignite/client/ignite_client_configuration.h>
+#include <string>
 
-#include <gtest/gtest.h>
-
-#include <chrono>
-
-using namespace ignite;
+namespace ignite {
 
 /**
- * Test suite.
+ * Ignite client authenticator. Provides authentication information during 
handshake.
  */
-class client_test : public ignite_runner_suite {};
-
-TEST_F(client_test, get_configuration) {
-    ignite_client_configuration cfg{get_node_addrs()};
-    cfg.set_logger(get_logger());
-    cfg.set_connection_limit(42);
-
-    auto client = ignite_client::start(cfg, std::chrono::seconds(30));
-
-    const auto &cfg2 = client.configuration();
-
-    EXPECT_EQ(cfg.get_endpoints(), cfg2.get_endpoints());
-    EXPECT_EQ(cfg.get_connection_limit(), cfg2.get_connection_limit());
-}
+class ignite_client_authenticator {
+public:
+    /**
+     * Get authenticator type.
+     *
+     * @return Authenticator type.
+     */
+    [[nodiscard]] virtual const std::string& get_type() const = 0;
+
+    /**
+     * Get identity.
+     *
+     * @return Identity.
+     */
+    [[nodiscard]] virtual const std::string& get_identity() const = 0;
+
+    /**
+     * Get secret.
+     *
+     * @return Secret.
+     */
+    [[nodiscard]] virtual const std::string& get_secret() const = 0;
+
+protected:
+    // Default
+    ignite_client_authenticator() = default;
+    virtual ~ignite_client_authenticator() = default;
+};
+
+} // namespace ignite
diff --git a/modules/platforms/cpp/ignite/client/ignite_client_configuration.h 
b/modules/platforms/cpp/ignite/client/ignite_client_configuration.h
index 6b8bc1942c..4aa725b1ca 100644
--- a/modules/platforms/cpp/ignite/client/ignite_client_configuration.h
+++ b/modules/platforms/cpp/ignite/client/ignite_client_configuration.h
@@ -18,6 +18,7 @@
 #pragma once
 
 #include <ignite/client/ignite_logger.h>
+#include <ignite/client/ignite_client_authenticator.h>
 
 #include <initializer_list>
 #include <memory>
@@ -42,7 +43,7 @@ public:
     /**
      * Constructor.
      *
-     * @param endpoint Endpoints list.
+     * @param endpoints Endpoints list.
      */
     ignite_client_configuration(std::initializer_list<std::string_view> 
endpoints)
         : m_endpoints(endpoints.begin(), endpoints.end()) {}
@@ -112,10 +113,30 @@ public:
      *
      * @see GetConnectionsLimit for details.
      *
-     * @param connectionsLimit Connections limit to set.
+     * @param limit Connections limit to set.
      */
     void set_connection_limit(uint32_t limit) { m_connection_limit = limit; }
 
+    /**
+     * Gets the authenticator.
+     *
+     * @see Also see basic_authenticator.
+     *
+     * @return Authenticator
+     */
+    [[nodiscard]] std::shared_ptr<ignite_client_authenticator> 
get_authenticator() const {
+        return m_authenticator;
+    }
+
+    /**
+     * Sets the authenticator.
+     *
+     * @param authenticator Authenticator. Do not use authentication if 
authenticator is @c nullptr.
+     */
+    void set_authenticator(std::shared_ptr<ignite_client_authenticator> 
authenticator) {
+        m_authenticator = std::move(authenticator);
+    }
+
 private:
     /** Endpoints. */
     std::vector<std::string> m_endpoints{"localhost"};
@@ -123,6 +144,9 @@ private:
     /** Logger. */
     std::shared_ptr<ignite_logger> m_logger{};
 
+    /** Authenticator. */
+    std::shared_ptr<ignite_client_authenticator> m_authenticator{};
+
     /** Active connections limit. */
     uint32_t m_connection_limit{0};
 };
diff --git a/modules/platforms/cpp/ignite/protocol/writer.h 
b/modules/platforms/cpp/ignite/protocol/writer.h
index 3cb3125062..2f5a95f230 100644
--- a/modules/platforms/cpp/ignite/protocol/writer.h
+++ b/modules/platforms/cpp/ignite/protocol/writer.h
@@ -29,6 +29,7 @@
 #include <memory>
 #include <msgpack.h>
 #include <string_view>
+#include <map>
 
 namespace ignite::protocol {
 
@@ -128,6 +129,19 @@ public:
      */
     void write_map_empty() { msgpack_pack_map(m_packer.get(), 0); }
 
+    /**
+     * Write map.
+     *
+     * @param values Map.
+     */
+    void write_map(const std::map<std::string, std::string> &values) {
+        msgpack_pack_map(m_packer.get(), values.size());
+        for (const auto &pair : values) {
+            msgpack_pack_str_with_body(m_packer.get(), pair.first.data(), 
pair.first.size());
+            msgpack_pack_str_with_body(m_packer.get(), pair.second.data(), 
pair.second.size());
+        }
+    }
+
     /**
      * Write bitset.
      *
diff --git a/modules/platforms/cpp/tests/client-test/CMakeLists.txt 
b/modules/platforms/cpp/tests/client-test/CMakeLists.txt
index 521b028675..29df8efd10 100644
--- a/modules/platforms/cpp/tests/client-test/CMakeLists.txt
+++ b/modules/platforms/cpp/tests/client-test/CMakeLists.txt
@@ -20,6 +20,7 @@ project(ignite-client-test)
 set(TARGET ${PROJECT_NAME})
 
 set(SOURCES
+    basic_authenticator_test.cpp
     compute_test.cpp
     gtest_logger.h
     ignite_client_test.cpp
diff --git 
a/modules/platforms/cpp/tests/client-test/basic_authenticator_test.cpp 
b/modules/platforms/cpp/tests/client-test/basic_authenticator_test.cpp
new file mode 100644
index 0000000000..d7e59944c9
--- /dev/null
+++ b/modules/platforms/cpp/tests/client-test/basic_authenticator_test.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+
+#include "ignite_runner_suite.h"
+
+#include <ignite/client/basic_authenticator.h>
+#include <ignite/client/ignite_client.h>
+#include <ignite/client/ignite_client_configuration.h>
+
+#include <gmock/gmock-matchers.h>
+#include <gtest/gtest.h>
+
+#include <chrono>
+
+using namespace ignite;
+
+/**
+ * Test suite.
+ */
+class basic_authenticator_test : public ignite_runner_suite
+{
+public:
+    /** Correct username */
+    inline static const std::string CORRECT_USERNAME{"user-1"};
+
+    /** Correct password */
+    inline static const std::string CORRECT_PASSWORD{"password-1"};
+
+    /**
+     * Tear down.
+     */
+    static void TearDownTestSuite() {
+        set_authentication_enabled(false);
+    }
+
+    /**
+     * Get default configuration.
+     *
+     * @return Configuration.
+     */
+    static ignite_client_configuration get_configuration() {
+        ignite_client_configuration cfg{get_node_addrs()};
+        cfg.set_logger(get_logger());
+
+        return cfg;
+    }
+
+    /**
+     * Get configuration with basic auth enabled.
+     *
+     * @param user Username to use.
+     * @param password Password to use.
+     * @return Configuration.
+     */
+    static ignite_client_configuration get_configuration(std::string user, 
std::string password) {
+        ignite_client_configuration cfg{get_configuration()};
+
+        auto authenticator = 
std::make_shared<basic_authenticator>(std::move(user), std::move(password));
+        cfg.set_authenticator(authenticator);
+
+        return cfg;
+    }
+
+    /**
+     * Get configuration with correct credentials.
+     *
+     * @return Configuration with correct credentials.
+     */
+    static ignite_client_configuration get_configuration_correct() {
+        return get_configuration(CORRECT_USERNAME, CORRECT_PASSWORD);
+    }
+
+    /**
+     * Change cluster authentication state.
+     *
+     * @param enable Authentication enabled.
+     */
+    static void set_authentication_enabled(bool enable) {
+        if (m_auth_enabled == enable)
+            return;
+
+        ignite_client_configuration cfg = m_auth_enabled ? 
get_configuration_correct() : get_configuration();
+
+        try {
+            auto client = ignite_client::start(cfg, std::chrono::seconds(30));
+            auto nodes = client.get_cluster_nodes();
+            client.get_compute().execute(nodes, ENABLE_AUTHN_JOB, {enable ? 1 
: 0});
+        } catch (const ignite_error&) {
+            // Ignore.
+            // As a result of this call, the client may be disconnected from 
the server due to authn config change.
+        }
+
+        // Wait for the server to apply the configuration change and drop the 
client connection.
+        std::this_thread::sleep_for(std::chrono::seconds(3));
+
+        m_auth_enabled = enable;
+    }
+
+private:
+    /** Authentication enabled. */
+    inline static bool m_auth_enabled{false};
+};
+
+TEST_F(basic_authenticator_test, disabled_on_server) {
+    set_authentication_enabled(false);
+    auto client = ignite_client::start(get_configuration_correct(), 
std::chrono::seconds(30));
+    (void) client.get_cluster_nodes();
+}
+
+TEST_F(basic_authenticator_test, disabled_on_client) {
+    set_authentication_enabled(true);
+    EXPECT_THROW(
+        {
+            try {
+                (void) ignite_client::start(get_configuration(), 
std::chrono::seconds(30));
+            } catch (const ignite_error &e) {
+                EXPECT_THAT(e.what_str(), testing::HasSubstr("Authentication 
failed"));
+                throw;
+            }
+        },
+        ignite_error);
+}
+
+TEST_F(basic_authenticator_test, success) {
+    set_authentication_enabled(true);
+    auto client = ignite_client::start(get_configuration_correct(), 
std::chrono::seconds(30));
+    (void) client.get_cluster_nodes();
+}
+
+TEST_F(basic_authenticator_test, wrong_username) {
+    set_authentication_enabled(true);
+    EXPECT_THROW(
+        {
+            try {
+                (void) ignite_client::start(get_configuration("Lorem Ipsum", 
"bla"), std::chrono::seconds(30));
+            } catch (const ignite_error &e) {
+                EXPECT_THAT(e.what_str(), testing::HasSubstr("Authentication 
failed"));
+                throw;
+            }
+        },
+        ignite_error);
+}
+
+TEST_F(basic_authenticator_test, wrong_password) {
+    set_authentication_enabled(true);
+    EXPECT_THROW(
+        {
+            try {
+                (void) 
ignite_client::start(get_configuration(CORRECT_USERNAME, "wrong"), 
std::chrono::seconds(30));
+            } catch (const ignite_error &e) {
+                EXPECT_THAT(e.what_str(), testing::HasSubstr("Authentication 
failed"));
+                throw;
+            }
+        },
+        ignite_error);
+}
diff --git a/modules/platforms/cpp/tests/client-test/compute_test.cpp 
b/modules/platforms/cpp/tests/client-test/compute_test.cpp
index 91d14489e0..e7f28a1ab4 100644
--- a/modules/platforms/cpp/tests/client-test/compute_test.cpp
+++ b/modules/platforms/cpp/tests/client-test/compute_test.cpp
@@ -40,10 +40,6 @@ protected:
         m_client = ignite_client::start(cfg, std::chrono::seconds(30));
     }
 
-    void TearDown() override {
-        // remove all
-    }
-
     /**
      * Get specific node.
      * @param id Node id.
diff --git a/modules/platforms/cpp/tests/client-test/ignite_client_test.cpp 
b/modules/platforms/cpp/tests/client-test/ignite_client_test.cpp
index 548dff30fa..999712a70c 100644
--- a/modules/platforms/cpp/tests/client-test/ignite_client_test.cpp
+++ b/modules/platforms/cpp/tests/client-test/ignite_client_test.cpp
@@ -17,6 +17,7 @@
 
 #include "ignite_runner_suite.h"
 
+#include <ignite/client/basic_authenticator.h>
 #include <ignite/client/ignite_client.h>
 #include <ignite/client/ignite_client_configuration.h>
 
@@ -42,4 +43,4 @@ TEST_F(client_test, get_configuration) {
 
     EXPECT_EQ(cfg.get_endpoints(), cfg2.get_endpoints());
     EXPECT_EQ(cfg.get_connection_limit(), cfg2.get_connection_limit());
-}
+}
\ No newline at end of file
diff --git a/modules/platforms/cpp/tests/client-test/ignite_runner_suite.h 
b/modules/platforms/cpp/tests/client-test/ignite_runner_suite.h
index 288a0f2bae..282be54181 100644
--- a/modules/platforms/cpp/tests/client-test/ignite_runner_suite.h
+++ b/modules/platforms/cpp/tests/client-test/ignite_runner_suite.h
@@ -46,6 +46,8 @@ public:
     inline static const std::string PLATFORM_TEST_NODE_RUNNER =
         "org.apache.ignite.internal.runner.app.PlatformTestNodeRunner";
 
+    inline static const std::string ENABLE_AUTHN_JOB = 
PLATFORM_TEST_NODE_RUNNER + "$EnableAuthenticationJob";
+
     inline static const std::string IT_THIN_CLIENT_COMPUTE_TEST =
         "org.apache.ignite.internal.runner.app.client.ItThinClientComputeTest";
 
diff --git a/modules/platforms/cpp/tests/client-test/main.cpp 
b/modules/platforms/cpp/tests/client-test/main.cpp
index 9d0c27809a..64d115eef2 100644
--- a/modules/platforms/cpp/tests/client-test/main.cpp
+++ b/modules/platforms/cpp/tests/client-test/main.cpp
@@ -87,12 +87,13 @@ int main(int argc, char **argv) {
         std::cout << "Tests run in a multi-node mode." << std::endl;
 
     ignite::IgniteRunner runner;
-    if (!check_test_node_connectable(std::chrono::seconds(5))) {
-        set_process_abort_handler([&](int signal) {
-            std::cout << "Caught signal " << signal << " during tests" << 
std::endl;
+    set_process_abort_handler([&](int signal) {
+        std::cout << "Caught signal " << signal << " during tests" << 
std::endl;
+
+        runner.stop();
+    });
 
-            runner.stop();
-        });
+    if (!check_test_node_connectable(std::chrono::seconds(5))) {
         runner.start();
         ensure_node_connectable(std::chrono::seconds(60));
     }

Reply via email to