Skip to content
Snippets Groups Projects
Verified Commit fdfc5367 authored by Timm Fitschen's avatar Timm Fitschen
Browse files

add configuration manager

parent 59c37487
Branches
Tags
1 merge request!1Minimal c interface
Showing
with 472 additions and 52 deletions
FROM debian:latest
FROM debian:buster-backports
RUN echo "deb http://deb.debian.org/debian buster-backports main" > /etc/apt/sources.list.d/buster-backports.list
RUN apt-get update
RUN apt-get install -y cmake/buster-backports
RUN apt-get install -y lcov
......@@ -14,8 +13,6 @@ RUN apt-get install -y openjdk-11-jdk-headless
WORKDIR /
COPY doc/requirements.txt doc-requirements.txt
RUN pip3 install -r doc-requirements.txt
COPY requirements.txt build-requirements.txt
RUN pip3 install -r build-requirements.txt
......
......@@ -230,7 +230,7 @@ if(_LINTING)
else()
message(STATUS "clang-tidy: ${clang_tidy}")
set(_CMAKE_CXX_CLANG_TIDY_CHECKS
"--checks=*,-fuchsia-*,-llvm-include-order,-llvmlibc-*")
"--checks=*,-fuchsia-*,-llvm-include-order,-llvmlibc-*,-readability-convert-member-functions-to-static,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-hicpp-no-array-decay,-llvm-else-after-return,-readability-else-after-return")
set(_CMAKE_CXX_CLANG_TIDY "${clang_tidy}"
"--header-filter=caosdb/.*[^\(\.pb\.h\)]$"
"--warnings-as-errors=*")
......@@ -338,7 +338,7 @@ install(FILES ${PROJECT_SOURCE_DIR}/caosdbConfigVersion.cmake
#######################################################
option(AUTOFORMATTING "call clang-format at configure time" ON)
if(AUTOFORMATTING)
file(GLOB format_test_sources test/*.cpp test/*.h)
file(GLOB format_test_sources test/*.cpp test/*.h test/*.h.in)
execute_process(COMMAND clang-format -i --verbose ${libcaosdb_INCL}
${libcaosdb_SRC} ${libcaosdb_TEST_SRC}
${PROJECT_SOURCE_DIR}/src/cxxcaosdbcli.cpp
......
......@@ -52,6 +52,24 @@ The coverage report can be viewed in a browser by opening
Please adhere to [Google's C++ naming conventions](https://google.github.io/styleguide/cppguide.html#Naming).
## Client Configuration
You can use a json file for the configuration of the client. See
`test/test_data/test_caosdb_client.json` for an example. You may use
`caosdb-client-configuration-schema.json` to validate your schema.
The client will load the configuration file from the first existing file in the following locations (predendence from highest to lowest):
1. A file specified by the environment variable `$CAOSDB_CLIENT_CONFIGURATION`.
2. `$PWD/caosdb_client.json`
3. `$PWD/caosdb-client.json`
4. `$PWD/.caosdb_client.json`
5. `$PWD/.caosdb-client.json`
6. `$HOME/caosdb_client.json`
7. `$HOME/caosdb-client.json`
8. `$HOME/.caosdb_client.json`
9. `$HOME/.caosdb-client.json`
## Documentation
In the build directory, run
......
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "CaosDB Client Configuration",
"description": "Configuration of the connection, logging, and other things of a CaosDB client.",
"type": "object",
"additionalProperties": false,
"properties": {
"connections": {
"type": "object",
"description": "Named connection configurations.",
"properties": {
"default": {
"oneOf": [
{
"type": "string",
"description": "Name of the default connection."
}, {
"$ref": "#/definitions/connection_configuration"
}
]
}
},
"additionalProperties": {
"$ref": "#/definitions/connection_configuration"
}
},
"extension": {
"type": "object",
"description": "A reserved configuration object which may be used to store additional options for particular clients and special extensions.",
"additionalProperties": true
}
},
"definitions": {
"connection_configuration": {
"type": "object",
"description": "A single connection configuration.",
"additionalProperties": false,
"properties": {
"name": { "type": "string" },
"host": {
"type": "string",
"description": "Domain name or ip address of the server host.",
"default": "localhost",
"examples": ["localhost", "caosdb.example.com", "192.168.0.123"]
},
"port": {
"type": "integer",
"description": "Ip port of the grpc end-point of the CaosDB server.",
"mininum": 1,
"default": 8443,
"maximum": 65535
},
"tls": {
"type": "boolean",
"description": "Indicates that the connection is using TLS (transport layer security) for authentication of the server and encryption of the communication. Setting this to 'false' is insecure unless other mechanism are in place to prevent a man-in-the-middle attack and eavesdropping by an unauthorized agent.",
"default": true
},
"server_certificate_path": {
"type": "string",
"description": "Relative or absolute path to a public certificate of a trusted CA (certificate authority) in PEM format. If not specified, the client system's default directory for certificates will be scanned (that is '/etc/ssl/certs/' in many linux distros).",
"default": null
},
"authentication": {
"type": "object",
"description": "Configuration of the user authentication. If the authentication property is not set, the client attempts to connect to the server as an anonymous user.",
"additionalProperties": false,
"properties": {
"type": {
"type": "string",
"enum": ["plain"]
},
"username": { "type": "string" },
"password": { "type": "string" },
"auth_token": { "type": "string" }
},
"allOf": [
{
"if": {"properties": {"type": { "pattern": "^plain$" } } },
"then": {"required": ["username", "password"] }
}
]
}
}
}
}
}
......@@ -20,6 +20,7 @@ class CaosdbConan(ConanFile):
def config_options(self):
if self.settings.os == "Windows":
del self.options.fPIC
self.options["boost"].without_python = True
# def source(self):
# self.run("git clone https://gitlab.indiscale.com/caosdb/src/caosdb-cpplib.git")
......
......@@ -21,8 +21,8 @@
find_package(Doxygen)
if (DOXYGEN_FOUND)
string(REPLACE ";" " " DOXYGEN_INPUT "${PROJECT_INCLUDE_DIR} ${CMAKE_BINARY_DIR}/include")
string(REPLACE ";" " " DOXYGEN_STRIP_FROM_PATH "${PROJECT_INCLUDE_DIR} ${CMAKE_BINARY_DIR}/include")
string(REPLACE ";" " " DOXYGEN_INPUT
"${libcaosdb_INCL} ${PROJECT_INCLUDE_DIR}/ccaosdb.h")
configure_file(Doxyfile.in Doxyfile)
......@@ -56,9 +56,14 @@ if (DOXYGEN_FOUND)
""
HEADER_FILE_NAME
${HEADER_FILE_NAME})
string(REPLACE
"caosdb/"
""
DOC_FILE_NAME
${HEADER_FILE_NAME})
configure_file(
header_file.rst.in
cppapi/_${HEADER_FILE_NAME}.rst)
cppapi/_${DOC_FILE_NAME}.rst)
endforeach ()
# create (plain) C docs
......
......@@ -19,7 +19,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
.. _api_root:
.. _capi_root:
C API
=====
......
......@@ -20,10 +20,12 @@
project = '@CMAKE_PROJECT_NAME@'
copyright = '2021 IndiScale GmbH'
author = 'Timm Fitschen'
version = '@CMAKE_PROJECT_VERSION@'
release = '@CMAKE_PROJECT_VERSION@'
rst_prolog = """
.. |PROJECT_NAME| replace:: @CMAKE_PROJECT_NAME@
.. |PROJECT_VERSION| replace:: @CMAKE_PROJECT_VERSION@
"""
......
......@@ -19,7 +19,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
.. _api_root:
.. _cppapi_root:
C++ API
=======
......
......@@ -25,11 +25,8 @@
Welcome to |PROJECT_NAME|'s documentation!
==========================================
Version: |PROJECT_VERSION|
This is work in progress.
.. toctree::
:maxdepth: 4
:caption: Contents:
......
alabaster==0.7.12
Babel==2.9.1
breathe==4.30.0
certifi==2020.12.5
chardet==4.0.0
docutils==0.16
idna==2.10
imagesize==1.2.0
Jinja2==2.11.3
MarkupSafe==1.1.1
packaging==20.9
Pygments==2.9.0
pyparsing==2.4.7
pytz==2021.1
requests==2.25.1
six==1.16.0
snowballstemmer==2.1.0
Sphinx==4.0.1
sphinx-rtd-theme==0.5.2
sphinx-sitemap==2.2.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==1.0.3
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.4
urllib3==1.26.4
......@@ -21,6 +21,7 @@
# add all header files to this list
set(libcaosdb_INCL
${CMAKE_CURRENT_SOURCE_DIR}/caosdb/authentication.h
${CMAKE_CURRENT_SOURCE_DIR}/caosdb/configuration.h
${CMAKE_CURRENT_SOURCE_DIR}/caosdb/connection.h
${CMAKE_CURRENT_BINARY_DIR}/caosdb/constants.h
${CMAKE_CURRENT_SOURCE_DIR}/caosdb/entity.h
......
......@@ -51,6 +51,7 @@ using grpc::string_ref;
*/
class Authenticator {
public:
virtual ~Authenticator() = default;
[[nodiscard]] virtual auto GetCallCredentials() const
-> std::shared_ptr<grpc::CallCredentials> = 0;
};
......
/*
* 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 CAOSDB_CONFIGURATION_H
#define CAOSDB_CONFIGURATION_H
#include <memory> // for unique_ptr
#include <string> // for string
#include "boost/filesystem/operations.hpp" // for exists
#include "boost/filesystem/path.hpp" // for path
#include "boost/json/object.hpp" // for object
#include "boost/json/value.hpp" // for value
#include "boost/json/value_ref.hpp" // for array, object
#include "caosdb/authentication.h" // for Authenticator, PlainPassw...
#include "caosdb/connection.h" // for ConnectionConfig, Certifi...
#include "caosdb/exceptions.h" // for ConfigurationError
#include "caosdb/utility.h" // for load_json_file
namespace caosdb::configuration {
using boost::filesystem::exists;
using boost::filesystem::path;
using boost::json::array;
using boost::json::object;
using boost::json::value;
using caosdb::authentication::Authenticator;
using caosdb::authentication::PlainPasswordAuthenticator;
using caosdb::connection::CertificateProvider;
using caosdb::connection::ConnectionConfig;
using caosdb::connection::ConnectionManager;
using caosdb::connection::InsecureConnectionConfig;
using caosdb::connection::PemFileCertificateProvider;
using caosdb::connection::TlsConnectionConfig;
using caosdb::exceptions::ConfigurationError;
using caosdb::utility::load_json_file;
/**
* Helper class (no state, just member functions) which should only be used by
* the ConfigurationManager to construct Connection instances from the stored
* configuration.
*/
class ConnectionConfigurationHelper {
public:
friend class ConfigurationManager;
private:
/**
* @param from - a single connection configuration.
*/
inline auto CreateCertificateProvider(const object &from) const
-> std::unique_ptr<CertificateProvider>;
/**
* @param from - a single connection configuration.
*/
auto CreateAuthenticator(const object &from) const
-> std::unique_ptr<Authenticator>;
/**
* @param from - a single connection configuration.
*/
auto
CreateConnectionConfiguration(const bool tls, const std::string &host,
const int port,
const CertificateProvider *certificate_provider,
const Authenticator *authenticator) const
-> std::unique_ptr<ConnectionConfig>;
/**
* @param from - a single connection configuration.
*/
auto IsTls(const object &from) const -> bool;
/**
* @param from - a single connection configuration.
*/
auto CreateConnectionConfiguration(const object &from) const
-> std::unique_ptr<ConnectionConfig>;
};
/**
* Reads the configuration file and keeps the configuration. Singleton.
*
* Currently, this class can only read a single configuration file. No merging
* or overwriting is supported.
*/
class ConfigurationManager {
public:
static ConfigurationManager &GetInstance() {
static ConfigurationManager instance;
return instance;
};
/**
* See mReset.
*/
inline static auto Reset() -> void { GetInstance().mReset(); }
/**
* See mClear.
*/
inline static auto Clear() -> void { GetInstance().mClear(); }
/**
* See mLoadSingleJSONConfiguration.
*/
inline static auto LoadSingleJSONConfiguration(const path &json_file)
-> void {
GetInstance().mLoadSingleJSONConfiguration(json_file);
}
/**
* See mGetConnectionConfiguration.
*/
inline static auto GetConnectionConfiguration(const std::string &name)
-> std::unique_ptr<ConnectionConfig> {
return GetInstance().mGetConnectionConfiguration(name);
}
/**
* Return the ConnectionConfig for the default connection.
*/
inline static auto GetDefaultConnectionConfiguration()
-> std::unique_ptr<ConnectionConfig> {
return GetInstance().mGetConnectionConfiguration(
GetInstance().mGetDefaultConnectionName());
}
/**
* See mGetDefaultConnectionName.
*/
inline static auto GetDefaultConnectionName() -> std::string {
return GetInstance().mGetDefaultConnectionName();
}
ConfigurationManager(ConfigurationManager const &) = delete;
void operator=(ConfigurationManager const &) = delete;
private:
value json_configuration;
ConnectionConfigurationHelper connection_configuration_helper;
inline ConfigurationManager() { InitializeDefaults(); };
/**
* Initialize this ConfigurationManager with the defaults.
*
* Currently, this means, that the ConfigurationManager attempts to load the
* first existing file from the LIBCAOSDB_CONFIGURATION_FILES_PRECEDENCE list
* of file locations.
*/
auto InitializeDefaults() -> void;
/**
* Return a json object representing the current configuration.
*/
auto GetConfiguration() const -> const object &;
/**
* Return the connection configurations.
*/
auto GetConnections() const -> const object &;
/**
* Return the configuration for the connection with the given name (as a json
* object).
*/
auto GetConnection(const std::string &name) const -> const object &;
/**
* Reset this ConfigurationManager.
*
* The current configuration is deleted and a new configuration is being
* loaded via InitializeDefaults.
*/
auto mReset() -> void;
/**
* Clear this ConfigurationManager.
*
* Afterwards, this ConfigurationManager is uninitilized.
*
* In contrast to mReset, this method only deletes the current configuration
* but does not load a new one via InitializeDefaults.
*/
auto mClear() -> void;
/**
* Load a configuration from a json file.
*/
auto mLoadSingleJSONConfiguration(const path &json_file) -> void;
/**
* Return the ConnectionConfig for the connection of the given name.
*/
auto mGetConnectionConfiguration(const std::string &name) const
-> std::unique_ptr<ConnectionConfig>;
/**
* Return the ConnectionConfig for the default connection.
*/
auto mGetDefaultConnectionName() const -> std::string;
};
} // namespace caosdb::configuration
#endif
......@@ -28,8 +28,9 @@
* @brief Configuration and setup of the connection.
*/
#include <iosfwd> // for ostream
#include <map> // for map
#include <memory> // for shared_ptr, unique_ptr
#include <string> // for string
#include <string> // for string, basic_string
#include "caosdb/authentication.h" // for Authenticator
#include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for EntityTransactionSe...
#include "caosdb/info.h" // for VersionInfo
......@@ -46,26 +47,27 @@ using caosdb::info::v1alpha1::GeneralInfoService;
using caosdb::transaction::Transaction;
using grpc::ChannelCredentials;
class CertificateificateProvider {
class CertificateProvider {
public:
[[nodiscard]] auto virtual GetCertificatePem() const -> std::string = 0;
virtual ~CertificateProvider() = default;
};
class PemFileCertificateProvider : public CertificateificateProvider {
class PemFileCertificateProvider : public CertificateProvider {
private:
std::string cacert;
std::string certificate_provider;
public:
explicit PemFileCertificateProvider(const std::string &path);
[[nodiscard]] auto GetCertificatePem() const -> std::string override;
};
class PemCertificateProvider : public CertificateificateProvider {
class PemCertificateProvider : public CertificateProvider {
private:
std::string cacert;
std::string certificate_provider;
public:
explicit PemCertificateProvider(const std::string &cacert);
explicit PemCertificateProvider(const std::string &certificate_provider);
[[nodiscard]] auto GetCertificatePem() const -> std::string override;
};
......@@ -79,6 +81,7 @@ private:
public:
ConnectionConfig(const std::string &host, int port);
virtual ~ConnectionConfig() = default;
friend auto operator<<(std::ostream &out, const ConnectionConfig &config)
-> std::ostream &;
......@@ -103,16 +106,16 @@ public:
class TlsConnectionConfig : public ConnectionConfig {
private:
std::shared_ptr<ChannelCredentials> credentials;
std::string cacert;
std::string certificate_provider;
public:
TlsConnectionConfig(const std::string &host, int port);
TlsConnectionConfig(const std::string &host, int port,
const Authenticator &authenticator);
TlsConnectionConfig(const std::string &host, int port,
const CertificateificateProvider &cacert);
const CertificateProvider &certificate_provider);
TlsConnectionConfig(const std::string &host, int port,
const CertificateificateProvider &cacert,
const CertificateProvider &certificate_provider,
const Authenticator &authenticator);
[[nodiscard]] auto GetChannelCredentials() const
-> std::shared_ptr<ChannelCredentials> override;
......@@ -134,5 +137,62 @@ public:
[[nodiscard]] auto GetVersionInfo() const -> std::unique_ptr<VersionInfo>;
[[nodiscard]] auto CreateTransaction() const -> std::unique_ptr<Transaction>;
};
/**
* Lazily creates and caches reusable connection instances. Singleton.
*
* This class delegates the configuration of new connections to the global
* ConfigurationManager.
*
* A reset of the ConfigurationManager also resets the ConnectionManager.
*
* @brief Lazily creates and caches reusable connection instances.
*/
class ConnectionManager {
private:
mutable std::map<std::string, std::shared_ptr<Connection>> connections;
mutable std::string default_connection_name;
inline ConnectionManager(){};
auto mHasConnection(const std::string &name) const -> bool;
auto mGetConnection(const std::string &name) const
-> const std::shared_ptr<Connection> &;
auto mGetDefaultConnection() const -> const std::shared_ptr<Connection> &;
inline auto mReset() -> void {
connections.clear();
default_connection_name = std::string();
}
public:
static ConnectionManager &GetInstance() {
static ConnectionManager instance;
return instance;
};
inline static auto HasConnection(const std::string &name) -> bool {
return ConnectionManager::GetInstance().mHasConnection(name);
};
inline static auto GetConnection(const std::string &name)
-> const std::shared_ptr<Connection> & {
return ConnectionManager::GetInstance().mGetConnection(name);
};
inline static auto GetDefaultConnection()
-> const std::shared_ptr<Connection> & {
return ConnectionManager::GetInstance().mGetDefaultConnection();
};
inline static auto Reset() -> void {
return ConnectionManager::GetInstance().mReset();
};
ConnectionManager(ConnectionManager const &) = delete;
void operator=(ConnectionManager const &) = delete;
};
} // namespace caosdb::connection
#endif
......@@ -33,6 +33,21 @@ const int COMPATIBLE_SERVER_VERSION_MAJOR = @libcaosdb_COMPATIBLE_SERVER_VERSION
const int COMPATIBLE_SERVER_VERSION_MINOR = @libcaosdb_COMPATIBLE_SERVER_VERSION_MINOR@;
const int COMPATIBLE_SERVER_VERSION_PATCH = @libcaosdb_COMPATIBLE_SERVER_VERSION_PATCH@;
const char* COMPATIBLE_SERVER_VERSION_PRE_RELEASE = "@libcaosdb_COMPATIBLE_SERVER_VERSION_PRE_RELEASE@";
/**
* Precedence of configuration files from highest to lowest.
*/
const char* LIBCAOSDB_CONFIGURATION_FILES_PRECEDENCE[] = {
"$CAOSDB_CLIENT_CONFIGURATION",
"caosdb_client.json",
"caosdb-client.json",
".caosdb_client.json",
".caosdb-client.json",
"$HOME/caosdb_client.json",
"$HOME/caosdb-client.json",
"$HOME/.caosdb_client.json",
"$HOME/.caosdb-client.json"
};
// clang-format on
#ifdef __cplusplus
} // namespace caosdb
......
......@@ -45,5 +45,23 @@ public:
: runtime_error(what_arg) {}
};
/**
* @brief The connection is known to the ConnectionManager under this name.
*/
class UnknownConnectionError : public runtime_error {
public:
explicit UnknownConnectionError(const std::string &what_arg)
: runtime_error(what_arg) {}
};
/**
* @brief Exception for errors of the ConnectionManager.
*/
class ConfigurationError : public runtime_error {
public:
explicit ConfigurationError(const std::string &what_arg)
: runtime_error(what_arg) {}
};
} // namespace caosdb::exceptions
#endif
......@@ -114,5 +114,11 @@ inline auto load_json_file(const path &json_file) -> value {
return parser.release();
}
inline auto get_home_directory() -> const path {
const auto *const home = getenv("HOME");
// TODO(tf) Add windowsy way of determining the home directory
return home;
}
} // namespace caosdb::utility
#endif
......@@ -192,6 +192,22 @@ int caosdb_connection_get_version_info(
caosdb_info_version_info *out,
const caosdb_connection_connection *connection);
/**
* Get the default connection from the ConnectionManager.
*
* The default connection is to be specified in a configuration file.
*/
int caosdb_connection_connection_manager_get_default_connection(
caosdb_connection_connection *out);
/**
* Get a named connection from the ConnectionManager.
*
* The named connection is to be specified in a configuration file.
*/
int caosdb_connection_connection_manager_get_connection(
caosdb_connection_connection *out, const char *name);
#ifdef __cplusplus
}
#endif
attrs==21.2.0
bottle==0.12.19
certifi==2021.5.30
chardet==4.0.0
......@@ -9,6 +10,7 @@ fasteners==0.16.3
future==0.18.2
idna==2.10
Jinja2==2.11.3
jsonschema==3.2.0
MarkupSafe==2.0.1
node-semver==0.6.1
packaging==20.9
......@@ -17,6 +19,7 @@ pluginbase==1.0.1
Pygments==2.9.0
PyJWT==1.7.1
pyparsing==2.4.7
pyrsistent==0.18.0
python-dateutil==2.8.1
PyYAML==5.4.1
requests==2.25.1
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment