191 lines
6.5 KiB
C++
191 lines
6.5 KiB
C++
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <cstring>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory_resource>
|
|
#include <optional>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
#include "utils.hpp"
|
|
|
|
namespace rublon {
|
|
class ConfigurationFactory;
|
|
|
|
class ConfigurationError {
|
|
public:
|
|
enum class Cause{NoDefaultValue};
|
|
const char * parameterName;
|
|
const char * cause;
|
|
};
|
|
|
|
class Configuration {
|
|
public:
|
|
struct Parameters {
|
|
std::string systemToken;
|
|
std::string secretKey;
|
|
std::string apiServer;
|
|
int prompt;
|
|
bool enablePasswdEmail;
|
|
bool logging;
|
|
bool autopushPrompt;
|
|
bool bypass;
|
|
bool offlineBypas;
|
|
} parameters;
|
|
};
|
|
|
|
namespace {
|
|
template < class C, typename T >
|
|
T member_ptr_t(T C::*v);
|
|
|
|
template < typename T >
|
|
tl::expected<T, ConfigurationError> to(std::string_view);
|
|
|
|
template <>
|
|
auto to(std::string_view arg) -> tl::expected<std::string, ConfigurationError> {
|
|
return std::string{arg.data(), arg.size()};
|
|
}
|
|
template <>
|
|
auto to(std::string_view arg) -> tl::expected<bool, ConfigurationError> {
|
|
return details::to_bool(arg);
|
|
}
|
|
template <>
|
|
auto to(std::string_view arg) -> tl::expected<int, ConfigurationError> {
|
|
return details::to_uint32(arg).value_or(0);
|
|
}
|
|
|
|
} // namespace
|
|
struct Entry {
|
|
enum class Source{UserInput,DefaultValue};
|
|
template < auto member >
|
|
static constexpr auto make_read_function() {
|
|
using pType = decltype(member_ptr_t(member));
|
|
|
|
return
|
|
[](const Entry * _this,
|
|
Configuration::Parameters * params,
|
|
std::string_view userInput) -> tl::expected< Source, ConfigurationError > {
|
|
const auto setDefaultValue = [&](const auto & value) -> tl::expected< Source, ConfigurationError >{
|
|
params->*member = value;
|
|
return Source::DefaultValue;
|
|
};
|
|
|
|
const auto setValue = [&](const auto & value) -> tl::expected< Source, ConfigurationError >{
|
|
params->*member = value;
|
|
return Source::UserInput;
|
|
};
|
|
|
|
const auto returnBadInput = [&](const auto &/*error*/) -> tl::expected< Source, ConfigurationError >{
|
|
return tl::unexpected{ConfigurationError{"",""}};
|
|
};
|
|
|
|
/// TODO error handling
|
|
if(userInput.empty()) {
|
|
if(_this->defaultValue != nullptr) {
|
|
return to< pType >(_this->defaultValue).and_then(setDefaultValue).or_else(returnBadInput);
|
|
} else {
|
|
return tl::unexpected{ConfigurationError{_this->name, "No default value"}};
|
|
}
|
|
}
|
|
|
|
return to< pType >(userInput).and_then(setValue).or_else(returnBadInput);
|
|
};
|
|
}
|
|
|
|
const char * name;
|
|
const char * defaultValue;
|
|
tl::expected< Source, ConfigurationError > (*_read)(const Entry * _this, Configuration::Parameters * params, std::string_view userInput);
|
|
|
|
bool read(Configuration::Parameters * params, std::optional<std::string_view> userInput) const{
|
|
constexpr const auto emptyString = "";
|
|
const auto logStored = [&](const auto & source) -> tl::expected< Source, ConfigurationError > {
|
|
if(source == Source::DefaultValue) {
|
|
rublon::log(LogLevel::Debug, "Parameter %s was set to {%s}, using default value", this->name, this->defaultValue);
|
|
} else {
|
|
rublon::log(LogLevel::Debug, "Parameter %s was set to {%s}", this->name, userInput.value().data());
|
|
}
|
|
return source;
|
|
};
|
|
|
|
const auto logError = [&](const auto & error) -> tl::expected< Source, ConfigurationError > {
|
|
rublon::log(LogLevel::Error, "Parameter %s is unavailable, aborting", this->name);
|
|
return tl::unexpected{error};
|
|
};
|
|
|
|
return _read(this, params, userInput.value_or(emptyString))
|
|
.and_then(logStored)
|
|
.or_else(logError)
|
|
.map([](const auto &) { return true; })
|
|
.map_error([](const auto &) { return false; }).value();
|
|
}
|
|
};
|
|
|
|
template < auto member >
|
|
constexpr auto make_entry(const char * name, const char * defaultValue) {
|
|
return Entry{name, defaultValue, Entry::make_read_function< member >()};
|
|
}
|
|
|
|
using P = Configuration::Parameters;
|
|
constexpr static inline std::array< Entry, 8 > configurationVariables = { //
|
|
make_entry< &P::logging >("logging", "true"),
|
|
make_entry< &P::systemToken >("systemToken", nullptr),
|
|
make_entry< &P::secretKey >("secretKey", nullptr),
|
|
make_entry< &P::apiServer >("rublonApiServer", nullptr),
|
|
make_entry< &P::prompt >("prompt", "1"),
|
|
make_entry< &P::enablePasswdEmail >("enablePasswdEmail", "true"),
|
|
make_entry< &P::autopushPrompt >("autopushPrompt", "false"),
|
|
make_entry< &P::offlineBypas >("failMode", "bypas")};
|
|
|
|
class ConfigurationFactory {
|
|
public:
|
|
ConfigurationFactory() = default;
|
|
|
|
std::optional< Configuration > systemConfig() {
|
|
memory::MonotonicStackResource< 8 * 1024 > stackResource;
|
|
using Params = Configuration::Parameters;
|
|
Params configValues;
|
|
|
|
std::ifstream file(std::filesystem::path{"/etc/rublon.config"});
|
|
if(not file.good())
|
|
return std::nullopt;
|
|
|
|
std::pmr::string line{&stackResource};
|
|
line.reserve(100);
|
|
std::pmr::map< std::pmr::string, std::pmr::string > parameters{&stackResource};
|
|
|
|
const auto readParameterByName = [&](std::string_view name) -> std::optional<std::string_view>{
|
|
return parameters.count(name.data()) ? std::optional< std::string_view >{parameters.at(name.data())} : std::nullopt;
|
|
};
|
|
|
|
while(std::getline(file, line)) {
|
|
std::pmr::string key{&stackResource};
|
|
std::pmr::string value{&stackResource};
|
|
|
|
if(!line.length())
|
|
continue;
|
|
|
|
if(line[0] == '#' || line[0] == ';')
|
|
continue;
|
|
|
|
auto posEqual = line.find('=');
|
|
key = line.substr(0, posEqual);
|
|
value = line.substr(posEqual + 1);
|
|
|
|
parameters[std::move(key)] = std::move(value);
|
|
}
|
|
|
|
for(const auto &entry : configurationVariables){
|
|
if(not entry.read(&configValues, readParameterByName(entry.name)))
|
|
return std::nullopt;
|
|
}
|
|
|
|
return Configuration{std::move(configValues)};
|
|
}
|
|
};
|
|
} // namespace rublon
|