diff --git a/cmake/FetchGRPC.cmake b/cmake/FetchGRPC.cmake index 7b4b36883dd8df9feef91a769f1559fc6d8f55ef..fffda8f90f55a342fe85fab54eefda5e428bea27 100644 --- a/cmake/FetchGRPC.cmake +++ b/cmake/FetchGRPC.cmake @@ -35,7 +35,8 @@ set(gRPC_BUILD_GRPC_PHP_PLUGIN OFF) set(gRPC_BUILD_GRPC_OBJECTIVE_C_PLUGIN OFF) set(gRPC_BUILD_GRPC_NODE_PLUGIN OFF) set(protobuf_BUILD_TESTS OFF) -set(vGRPC_TAG_VERSION_OF_YOUR_CHOICE "v1.38.1") +set(gRPC_VERSION "1.38.1") +set(vGRPC_TAG_VERSION_OF_YOUR_CHOICE "v${gRPC_VERSION}") if(GRPC_AS_SUBMODULE) # One way to build a projects that uses gRPC is to just include the @@ -209,8 +210,8 @@ else() # Find gRPC installation # Looks for gRPCConfig.cmake file installed by gRPC's cmake installation. - find_package(gRPC CONFIG REQUIRED) - message(STATUS "Using gRPC ${gRPC_VERSION}") + find_package(gRPC ${gRPC_VERSION} EXACT CONFIG REQUIRED) + message(STATUS "Using gRPC ${gRPC_VERSION} at ${gRPC}") set(_GRPC_GRPCPP gRPC::grpc++) if(CMAKE_CROSSCOMPILING) diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 1b6533fc92baf4412ce5eccf1a9ffe2ecdf26144..0e4d0dae38b5689372a0424a590fe16504fc3b87 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -22,6 +22,7 @@ set(libcaosdb_INCL ${CMAKE_CURRENT_BINARY_DIR}/constants.h ${CMAKE_CURRENT_SOURCE_DIR}/connection.h + ${CMAKE_CURRENT_SOURCE_DIR}/utils.h ) # pass variable to parent scope diff --git a/include/connection.h b/include/connection.h index 7bd14f053ef748dee3a162ba7683f5567581ab8f..d4d5d74a4a897ea98bd52b3acdf2e7c40d9fa885 100644 --- a/include/connection.h +++ b/include/connection.h @@ -1,5 +1,4 @@ /* - * * This file is a part of the CaosDB Project. * * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> @@ -37,16 +36,40 @@ #include <grpcpp/create_channel.h> #include <grpcpp/security/credentials.h> #include "caosdb/info/v1alpha1/main.grpc.pb.h" +namespace grpc { class ChannelCredentials; } +namespace caosdb { namespace info { namespace v1alpha1 { class VersionInfo; } } } namespace caosdb { using caosdb::info::v1alpha1::GeneralInfoService; using caosdb::info::v1alpha1::VersionInfo; using grpc::ChannelCredentials; +class CACertificateProvider { +public: + [[nodiscard]] auto virtual getCACertPem() const -> std::string = 0; +}; + +class PemFileCACertProvider : public CACertificateProvider { +private: + std::string cacert; +public: + explicit PemFileCACertProvider(const std::string &path); + [[nodiscard]] auto getCACertPem() const -> std::string override; +}; + +class PemCACertProvider : public CACertificateProvider { +private: + std::string cacert; +public: + explicit PemCACertProvider(const std::string &cacert); + [[nodiscard]] auto getCACertPem() const -> std::string override; +}; + /** * @brief Configuration of the CaosDB connection. */ class CaosDBConnectionConfig { +private: std::string host; int port; @@ -59,19 +82,16 @@ public: [[nodiscard]] auto virtual toString() const -> std::string = 0; [[nodiscard]] auto getHost() const -> std::string; [[nodiscard]] auto getPort() const -> int; - [[nodiscard]] auto virtual getChannelCredentials() const - -> std::shared_ptr<ChannelCredentials> = 0; + [[nodiscard]] auto virtual getChannelCredentials() const -> std::shared_ptr<ChannelCredentials> = 0; }; class InsecureCaosDBConnectionConfig : public CaosDBConnectionConfig { private: std::shared_ptr<ChannelCredentials> credentials; - public: InsecureCaosDBConnectionConfig(const std::string &host, int port); + [[nodiscard]] auto getChannelCredentials() const -> std::shared_ptr<ChannelCredentials> override; [[nodiscard]] auto toString() const -> std::string override; - [[nodiscard]] auto getChannelCredentials() const - -> std::shared_ptr<ChannelCredentials> override; }; class SslCaosDBConnectionConfig : public CaosDBConnectionConfig { @@ -79,9 +99,10 @@ private: std::shared_ptr<ChannelCredentials> credentials; public: - SslCaosDBConnectionConfig(const std::string &host, int port); SslCaosDBConnectionConfig(const std::string &host, int port, - const std::string &cacert); + const std::shared_ptr<CACertificateProvider> &cacert); + [[nodiscard]] auto getChannelCredentials() const -> std::shared_ptr<ChannelCredentials> override; + [[nodiscard]] auto toString() const -> std::string override; }; /** diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..616d2b9d69c64fd370b2bda8aec0b3db4e1fd2c4 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,66 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#ifndef UTILS_H +#define UTILS_H +#include <string_view> +#include <fstream> +#include <string> +#include <cstdlib> + +namespace caosdb::utils { + +/** + * @brief Read a text file into a string and return the file's content. + * @todo use boost-filesystem's "load_string_file"! + */ +inline auto load_string_file(const std::string &path) -> std::string { + const auto path_view = std::string_view{path}; + constexpr auto size = std::size_t{4096}; + auto stream = std::ifstream{path_view.data()}; + stream.exceptions(std::ios_base::badbit); + + auto result = std::string(); + auto buffer = std::string(size, '\0'); + while (stream.read(& buffer[0], size)) { + result.append(buffer, 0, stream.gcount()); + } + result.append(buffer, 0, stream.gcount()); + return result; +} + +/** + * @brief Return the value of an environment variable or - if undefined - the + * fall_back value. + */ +inline auto get_env_var(const std::string & key, const std::string & fall_back ) -> const std::string +{ + const char * val = getenv( key.c_str() ); + if ( val == nullptr ) { + return fall_back; + } else { + const auto result = std::string(val); + return result; + } +} + +} // namespace caosdb::utils +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 21a98c489b8627c22d9d30e2300ad3b1ac655cbd..49f850005698c5a18c2956a4689e50fc06249209 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,7 @@ # add all source files to this list set(libcaosdb_SRC + src/utils.cpp src/connection.cpp ) diff --git a/src/caosdbcli.cpp b/src/caosdbcli.cpp index e5c95da3763c75882ee9ae97d9dbb40752ec27d9..66d1315218e7307dee810fb115238c972b9087b7 100644 --- a/src/caosdbcli.cpp +++ b/src/caosdbcli.cpp @@ -27,6 +27,7 @@ #include "caosdb/info/v1alpha1/main.pb.h" #include "constants.h" #include "connection.h" +#include "utils.h" auto main() -> int { @@ -35,22 +36,22 @@ auto main() -> int { << caosdb::LIBCAOSDB_VERSION_PATCH << ")" << std::endl; std::cout << "We don't miss the H of caos." << std::endl; - const std::string host = "localhost"; - // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) - const int port = 8080; - const std::string &test = host; - std::cout << test << std::endl; + const auto pem_file_path = caosdb::utils::get_env_var("CAOSDB_SERVER_CA_PEM", std::string()); + std::string pem = caosdb::utils::load_string_file(pem_file_path); - std::shared_ptr<caosdb::InsecureCaosDBConnectionConfig> config = - std::make_shared<caosdb::InsecureCaosDBConnectionConfig>(host, port); + std::cout << "PEM" << pem << "\n"; + const std::string host = caosdb::utils::get_env_var("CAOSDB_SERVER_HOST", "localhost"); + const std::string port_str = caosdb::utils::get_env_var("CAOSDB_SERVER_PORT", "8000"); + const int port = std::stoi(port_str); + + auto cacert = std::make_shared<caosdb::PemCACertProvider>(pem); + auto config = std::make_shared<caosdb::SslCaosDBConnectionConfig>(host, port, cacert); caosdb::CaosDBConnection connection(config); std::cout << std::endl << connection << std::endl; - const caosdb::info::v1alpha1::VersionInfo &v_info = - connection.getVersionInfo(); - const std::string &build = v_info.build(); - std::cout << "BUILD(" << build[0] << ")" << std::endl; + const auto &v_info = connection.getVersionInfo(); + const auto &build = v_info.build(); + //std::cout << "BUILD(" << build[0] << ")" << std::endl; std::cout << "VersionInfo(" << v_info.major() << "." << v_info.minor() << "." << v_info.patch() << ")" << std::endl; - std::cout << "PRE_RELEASE(" << v_info.pre_release() << ")" << std::endl; return 0; } diff --git a/src/connection.cpp b/src/connection.cpp index 42ef66eb551fcc2953b652add80d7a7d6b51527e..62b71dd3b96ec46e5536019acad3ef6e3d1d7fd1 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -21,6 +21,7 @@ */ #include "connection.h" +#include "utils.h" #include <grpcpp/create_channel.h> #include <grpcpp/impl/codegen/client_context.h> #include <grpcpp/impl/codegen/status.h> @@ -32,6 +33,7 @@ #include "caosdb/info/v1alpha1/main.pb.h" namespace caosdb { +using caosdb::utils::load_string_file; using caosdb::info::v1alpha1::GeneralInfoService; using caosdb::info::v1alpha1::GetVersionInfoRequest; using caosdb::info::v1alpha1::GetVersionInfoResponse; @@ -40,6 +42,22 @@ using grpc::InsecureChannelCredentials; using grpc::SslCredentials; using grpc::SslCredentialsOptions; +PemFileCACertProvider::PemFileCACertProvider(const std::string &path) { + this->cacert = load_string_file(path); +} + +auto PemFileCACertProvider::getCACertPem() const -> std::string { + return this->cacert; +} + +PemCACertProvider::PemCACertProvider(const std::string &cacert) { + this->cacert = cacert; +} + +auto PemCACertProvider::getCACertPem() const -> std::string { + return this->cacert; +} + CaosDBConnectionConfig::CaosDBConnectionConfig(const std::string &host, int port) { this->host = host; @@ -61,12 +79,7 @@ auto operator<<(std::ostream &out, const CaosDBConnectionConfig &config) InsecureCaosDBConnectionConfig::InsecureCaosDBConnectionConfig( const std::string &host, int port) : CaosDBConnectionConfig(host, port) { - this->credentials = grpc::InsecureChannelCredentials(); -} - -auto InsecureCaosDBConnectionConfig::toString() const -> std::string { - return "InsecureCaosDBConnectionConfig(" + this->getHost() + "," + - std::to_string(this->getPort()) + ")"; + this->credentials = grpc::InsecureChannelCredentials(); } auto InsecureCaosDBConnectionConfig::getChannelCredentials() const @@ -74,26 +87,34 @@ auto InsecureCaosDBConnectionConfig::getChannelCredentials() const return this->credentials; } -SslCaosDBConnectionConfig::SslCaosDBConnectionConfig( - const std::string &host, int port, const std::string &cacert) - : CaosDBConnectionConfig(host, port) { - auto options = SslCredentialsOptions(); - options.pem_root_certs = cacert; - this->credentials = SslCredentials(options); +auto InsecureCaosDBConnectionConfig::toString() const -> std::string { + return "InsecureCaosDBConnectionConfig(" + this->getHost() + "," + + std::to_string(this->getPort()) + ")"; } SslCaosDBConnectionConfig::SslCaosDBConnectionConfig( - const std::string &host, int port) + const std::string &host, int port, const std::shared_ptr<CACertificateProvider> &cacert) : CaosDBConnectionConfig(host, port) { - auto options = SslCredentialsOptions(); + SslCredentialsOptions options; + options.pem_root_certs = cacert->getCACertPem(); this->credentials = SslCredentials(options); } +auto SslCaosDBConnectionConfig::getChannelCredentials() const + -> std::shared_ptr<ChannelCredentials> { + return this->credentials; +} + +auto SslCaosDBConnectionConfig::toString() const -> std::string { + return "SslCaosDBConnectionConfig(" + this->getHost() + "," + + std::to_string(this->getPort()) + ")"; +} + + CaosDBConnection::CaosDBConnection( const std::shared_ptr<CaosDBConnectionConfig> &config) { this->config = config; - const std::string &target = - this->config->getHost() + ":" + std::to_string(this->config->getPort()); + const std::string target = this->config->getHost() + ":" + std::to_string(this->config->getPort()); const std::shared_ptr<grpc::Channel> &channel = grpc::CreateChannel(target, this->config->getChannelCredentials()); this->stub_ = GeneralInfoService::NewStub(channel); diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bfb4049278fe29856fe48ce40016311354ea412f --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,6 @@ +#include "utils.h" + +namespace caosdb::utils { + + +} // namespace caosdb::utils diff --git a/test/test_connection.cpp b/test/test_connection.cpp index b8d4d53322e58fccad21e68c051769c207266a55..d1a41570c1e2674a40548e84d815597355191a82 100644 --- a/test/test_connection.cpp +++ b/test/test_connection.cpp @@ -27,7 +27,7 @@ #include <type_traits> #include "gtest/gtest_pred_impl.h" -TEST(test_connection, localhost_8080) { +TEST(test_connection, configure_insecure_localhost_8080) { caosdb::InsecureCaosDBConnectionConfig config("localhost", 8000); ASSERT_EQ("localhost", config.getHost()); @@ -36,3 +36,14 @@ TEST(test_connection, localhost_8080) { config.getChannelCredentials(); ASSERT_TRUE(icc != nullptr); } + +TEST(test_connection, configure_ssl_localhost_8080) { + auto cacert = std::make_shared<caosdb::PemCACertProvider>("ca chain"); + caosdb::SslCaosDBConnectionConfig config("localhost", 44300, cacert); + + ASSERT_EQ("localhost", config.getHost()); + ASSERT_EQ(44300, config.getPort()); + std::shared_ptr<grpc::ChannelCredentials> sslcc = + config.getChannelCredentials(); + ASSERT_TRUE(sslcc != nullptr); +}