bwi/v2.3.0 #29
@ -1,14 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <rublon/memory.hpp>
|
||||||
#include <rublon/error.hpp>
|
#include <rublon/error.hpp>
|
||||||
#include <rublon/static_string.hpp>
|
#include <rublon/static_string.hpp>
|
||||||
#include <rublon/utils.hpp>
|
#include <rublon/utils.hpp>
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
template < typename T >
|
#include <fstream>
|
||||||
constexpr bool is_static_string_v = std::is_base_of_v< rublon::details::StaticStringBase, T >;
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
static_assert(is_static_string_v< rublon::StaticString< 32 > >);
|
|
||||||
|
|
||||||
namespace rublon {
|
namespace rublon {
|
||||||
class ConfigurationFactory;
|
class ConfigurationFactory;
|
||||||
@ -16,11 +16,16 @@ class ConfigurationFactory;
|
|||||||
enum class FailMode { bypass, deny };
|
enum class FailMode { bypass, deny };
|
||||||
|
|
||||||
class Configuration {
|
class Configuration {
|
||||||
|
private:
|
||||||
|
std::pmr::memory_resource * memoryResource;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Configuration() : memoryResource{memory::default_resource()} {}
|
||||||
|
|
||||||
// change to StaticString
|
// change to StaticString
|
||||||
StaticString< 32 > systemToken{};
|
std::pmr::string systemToken{memoryResource};
|
||||||
StaticString< 32 > secretKey{};
|
std::pmr::string secretKey{memoryResource};
|
||||||
StaticString< 4096 > apiServer{};
|
std::pmr::string apiServer{memoryResource};
|
||||||
|
|
||||||
int prompt{};
|
int prompt{};
|
||||||
bool enablePasswdEmail{};
|
bool enablePasswdEmail{};
|
||||||
@ -29,168 +34,36 @@ class Configuration {
|
|||||||
FailMode failMode{};
|
FailMode failMode{};
|
||||||
bool nonInteractiveMode{};
|
bool nonInteractiveMode{};
|
||||||
|
|
||||||
StaticString< 8 > proxyType{};
|
std::optional< std::pmr::string > proxyType{memoryResource};
|
||||||
StaticString< 4096 > proxyServer{};
|
std::optional< std::pmr::string > proxyServer{memoryResource};
|
||||||
StaticString< 256 > proxyUsername{};
|
std::optional< std::pmr::string > proxyUsername{memoryResource};
|
||||||
StaticString< 256 > proxyPass{};
|
std::optional< std::pmr::string > proxyPass{memoryResource};
|
||||||
int proxyPort{};
|
std::optional< int > proxyPort{};
|
||||||
bool proxyAuthRequired{};
|
bool proxyAuthRequired{}; // defaulted
|
||||||
bool proxyEnabled{};
|
bool proxyEnabled{}; // defaulted
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
|
||||||
template < class C, typename T >
|
|
||||||
T member_ptr_t(T C::*v);
|
|
||||||
|
|
||||||
template < typename T >
|
class ConfigurationReader {
|
||||||
tl::expected< T, ConfigurationError > to(std::string_view);
|
|
||||||
|
|
||||||
template < class T >
|
|
||||||
auto to_string(std::string_view arg) -> tl::expected< T, ConfigurationError > {
|
|
||||||
T value{};
|
|
||||||
assert(arg.size() <= (value.size() - 1));
|
|
||||||
std::memcpy(value.data(), arg.data(), arg.size());
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
auto to(std::string_view arg) -> tl::expected< bool, ConfigurationError > {
|
|
||||||
return conv::to_bool(arg);
|
|
||||||
}
|
|
||||||
template <>
|
|
||||||
auto to(std::string_view arg) -> tl::expected< int, ConfigurationError > {
|
|
||||||
return conv::to_uint32(arg).value_or(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
auto to(std::string_view arg) -> tl::expected< FailMode, ConfigurationError > {
|
|
||||||
if(arg == "bypass")
|
|
||||||
return FailMode::bypass;
|
|
||||||
if(arg == "deny")
|
|
||||||
return FailMode::deny;
|
|
||||||
return tl::unexpected{ConfigurationError{ConfigurationError::ErrorClass::BadFailMode}};
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename T >
|
|
||||||
auto parse(std::string_view arg) -> tl::expected< T, ConfigurationError > {
|
|
||||||
if(arg.empty()) {
|
|
||||||
return tl::unexpected{ConfigurationError::ErrorClass::Empty};
|
|
||||||
} else {
|
|
||||||
if constexpr(is_static_string_v< T >) {
|
|
||||||
return to_string< T >(arg);
|
|
||||||
} else {
|
|
||||||
return to< T >(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // 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 * configuration, std::string_view userInput) -> tl::expected< Source, ConfigurationError > {
|
|
||||||
const auto setDefaultValue = [&](const ConfigurationError & error) -> tl::expected< Source, ConfigurationError > {
|
|
||||||
log(LogLevel::Warning, "applying user provided value for %s parameter, faild with %s", _this->name, error.what());
|
|
||||||
if(_this->defaultValue != nullptr) {
|
|
||||||
configuration->*member = parse< pType >(_this->defaultValue).value();
|
|
||||||
return Source::DefaultValue;
|
|
||||||
} else {
|
|
||||||
log(LogLevel::Error, "parameter %s has not been found and has no default value", _this->name);
|
|
||||||
if(userInput.empty())
|
|
||||||
return tl::unexpected{ConfigurationError::ErrorClass::RequiredValueNotFound};
|
|
||||||
else
|
|
||||||
return tl::unexpected{ConfigurationError::ErrorClass::BadInput};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto saveValue = [&](const auto & value) -> tl::expected< Source, ConfigurationError > {
|
|
||||||
configuration->*member = value;
|
|
||||||
return Source::UserInput;
|
|
||||||
};
|
|
||||||
|
|
||||||
return parse< pType >(userInput).and_then(saveValue).or_else(setDefaultValue);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const char * name;
|
|
||||||
const char * defaultValue;
|
|
||||||
tl::expected< Source, ConfigurationError > (*_read)(const Entry * _this, Configuration * configuration, std::string_view userInput);
|
|
||||||
|
|
||||||
bool read(Configuration * configuration, std::optional< std::string_view > userInput) const {
|
|
||||||
constexpr const auto emptyString = "";
|
|
||||||
const auto logStored = [&](const auto & source) -> tl::expected< Source, ConfigurationError > {
|
|
||||||
rublon::log(LogLevel::Debug,
|
|
||||||
"Configuration parameter '%s' was set to '%s'%s",
|
|
||||||
this->name,
|
|
||||||
this->defaultValue,
|
|
||||||
source == Source::DefaultValue ? " (default)" : "");
|
|
||||||
return source;
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto logError = [&](const auto & error) -> tl::expected< Source, ConfigurationError > {
|
|
||||||
rublon::log(LogLevel::Error,
|
|
||||||
"Configuration parameter '%s' has no default value and is not provided in user configuraion, aborting",
|
|
||||||
this->name);
|
|
||||||
return tl::unexpected{error};
|
|
||||||
};
|
|
||||||
|
|
||||||
return _read(this, configuration, userInput.value_or(emptyString)).and_then(logStored).or_else(logError).has_value();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template < auto member >
|
|
||||||
constexpr auto make_entry(const char * name, const char * defaultValue) {
|
|
||||||
return Entry{name, defaultValue, Entry::make_read_function< member >()};
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr static inline std::array< Entry, 16 > configurationVariables = {
|
|
||||||
make_entry< &Configuration::logging >("logging", "true"),
|
|
||||||
make_entry< &Configuration::systemToken >("systemToken", nullptr),
|
|
||||||
make_entry< &Configuration::secretKey >("secretKey", nullptr),
|
|
||||||
make_entry< &Configuration::apiServer >("rublonApiServer", nullptr),
|
|
||||||
make_entry< &Configuration::prompt >("prompt", "1"),
|
|
||||||
make_entry< &Configuration::enablePasswdEmail >("enablePasswdEmail", "true"),
|
|
||||||
make_entry< &Configuration::autopushPrompt >("autopushPrompt", "false"),
|
|
||||||
make_entry< &Configuration::failMode >("failMode", "deny"),
|
|
||||||
make_entry< &Configuration::nonInteractiveMode >("nonInteractiveMode", "false"),
|
|
||||||
|
|
||||||
make_entry< &Configuration::proxyType >("proxyType", nullptr),
|
|
||||||
make_entry< &Configuration::proxyServer >("proxyServer", nullptr),
|
|
||||||
make_entry< &Configuration::proxyUsername >("proxyUsername", nullptr),
|
|
||||||
make_entry< &Configuration::proxyPass >("proxyPass", nullptr),
|
|
||||||
make_entry< &Configuration::proxyPort >("proxyPort", "8080"),
|
|
||||||
make_entry< &Configuration::proxyAuthRequired >("proxyAuthRequired", "false"),
|
|
||||||
make_entry< &Configuration::proxyEnabled >("proxyEnabled", "false") //
|
|
||||||
};
|
|
||||||
|
|
||||||
class ConfigurationFactory {
|
|
||||||
public:
|
public:
|
||||||
ConfigurationFactory() = default;
|
ConfigurationReader(std::pmr::memory_resource * memResource = memory::default_resource()) : memoryResource(memResource) {}
|
||||||
|
|
||||||
std::optional< Configuration > systemConfig() {
|
// Load config from file path
|
||||||
memory::MonotonicStackResource< 16 * 1024 > stackResource;
|
bool loadFromFile(const std::string & filepath) {
|
||||||
Configuration configuration{};
|
using namespace memory::literals;
|
||||||
|
memory::MonotonicStackResource< 8_kB > stackResource;
|
||||||
std::ifstream file(std::filesystem::path{"/etc/rublon.config"});
|
std::ifstream file(filepath);
|
||||||
if(not file.good())
|
if(not file.good())
|
||||||
return std::nullopt;
|
return false;
|
||||||
|
|
||||||
std::pmr::string line{&stackResource};
|
std::pmr::string line{&stackResource};
|
||||||
line.reserve(100);
|
line.reserve(8000); // allocate full stack to line
|
||||||
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)) {
|
while(std::getline(file, line)) {
|
||||||
std::pmr::string key{&stackResource};
|
std::pmr::string key{memoryResource};
|
||||||
std::pmr::string value{&stackResource};
|
std::pmr::string value{memoryResource};
|
||||||
|
|
||||||
|
details::trimInPlace(line);
|
||||||
if(!line.length())
|
if(!line.length())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -201,15 +74,147 @@ class ConfigurationFactory {
|
|||||||
key = line.substr(0, posEqual);
|
key = line.substr(0, posEqual);
|
||||||
value = line.substr(posEqual + 1);
|
value = line.substr(posEqual + 1);
|
||||||
|
|
||||||
parameters[std::move(key)] = std::move(value);
|
keyValues[std::move(key)] = std::move(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto & entry : configurationVariables) {
|
return true;
|
||||||
if(not entry.read(&configuration, readParameterByName(entry.name)))
|
}
|
||||||
|
|
||||||
|
// Load values into Configuration object, with defaults provided
|
||||||
|
tl::expected< bool, ConfigurationError > applyTo(Configuration & config) {
|
||||||
|
// Helper lambdas for conversion
|
||||||
|
using string = std::pmr::string;
|
||||||
|
|
||||||
|
auto getStringOpt = [&](const string & key) -> std::optional< std::pmr::string > {
|
||||||
|
auto it = keyValues.find(key);
|
||||||
|
if(it == keyValues.end()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return string{it->second.data(), it->second.size(), memoryResource};
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getStringReq = [&](const string & key) -> tl::expected< std::pmr::string, ConfigurationError > {
|
||||||
|
auto val = getStringOpt(key);
|
||||||
|
if(val.has_value())
|
||||||
|
return std::move(val.value());
|
||||||
|
return tl::unexpected{ConfigurationError::ErrorClass::RequiredValueNotFound};
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getInt = [&](const string & key) -> std::optional< int > {
|
||||||
|
auto it = keyValues.find(key);
|
||||||
|
if(it == keyValues.end())
|
||||||
|
return std::nullopt;
|
||||||
|
return conv::to_uint32opt(it->second);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getBool = [&](const string & key) -> std::optional< bool > {
|
||||||
|
memory::MonotonicStackResource< 64 > memoryResource;
|
||||||
|
auto it = keyValues.find(key);
|
||||||
|
if(it == keyValues.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (it->second.size() > 5 ){
|
||||||
|
log(LogLevel::Warning, "Configuration value %s is too long, please check", key.c_str());
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pmr::string val{&memoryResource};
|
||||||
|
val = it->second;
|
||||||
|
std::transform(val.begin(), val.end(), val.begin(), [](unsigned char c) { return static_cast< char >(std::tolower(c)); });
|
||||||
|
|
||||||
|
if(val == "1" || val == "true" || val == "yes" || val == "on")
|
||||||
|
return true;
|
||||||
|
if(val == "0" || val == "false" || val == "no" || val == "off")
|
||||||
|
return false;
|
||||||
|
return std::nullopt;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getFailMode = [&](const string & key) -> std::optional< FailMode > {
|
||||||
|
auto it = keyValues.find(key);
|
||||||
|
if(it == keyValues.end())
|
||||||
|
return std::nullopt;
|
||||||
|
auto val = it->second;
|
||||||
|
if(val == "bypass")
|
||||||
|
return FailMode::bypass;
|
||||||
|
if(val == "deny")
|
||||||
|
return FailMode::deny;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reading required fields
|
||||||
|
if(auto val = getStringReq("systemToken"); not val.has_value())
|
||||||
|
return tl::unexpected{val.error()};
|
||||||
|
else
|
||||||
|
config.systemToken = std::move(val.value());
|
||||||
|
|
||||||
|
if(auto val = getStringReq("secretKey"); not val.has_value())
|
||||||
|
return tl::unexpected{val.error()};
|
||||||
|
else
|
||||||
|
config.secretKey = std::move(val.value());
|
||||||
|
|
||||||
|
if(auto val = getStringReq("rublonApiServer"); not val.has_value())
|
||||||
|
return tl::unexpected{val.error()};
|
||||||
|
else
|
||||||
|
config.apiServer = std::move(val.value());
|
||||||
|
|
||||||
|
// optional
|
||||||
|
config.prompt = getInt("prompt").value_or(1);
|
||||||
|
config.enablePasswdEmail = getBool("enablePasswdEmail").value_or(true);
|
||||||
|
config.logging = getBool("logging").value_or(true);
|
||||||
|
config.autopushPrompt = getBool("autopushPrompt").value_or(false);
|
||||||
|
config.nonInteractiveMode = getBool("nonInteractiveMode").value_or(false);
|
||||||
|
config.failMode = getFailMode("failMode").value_or(FailMode::deny);
|
||||||
|
|
||||||
|
// reading proxy configuration
|
||||||
|
config.proxyEnabled = getBool("proxyEnabled").value_or(false);
|
||||||
|
config.proxyType = getStringOpt("proxyType");
|
||||||
|
config.proxyServer = getStringOpt("proxyServer");
|
||||||
|
if(config.proxyEnabled) {
|
||||||
|
if(not config.proxyType) {
|
||||||
|
log(LogLevel::Error, "Proxy is enabled but proxy type is not set");
|
||||||
|
return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration};
|
||||||
|
}
|
||||||
|
if(not config.proxyServer) {
|
||||||
|
log(LogLevel::Error, "Proxy is enabled but proxy server is not set");
|
||||||
|
return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.proxyAuthRequired = getBool("proxyAuthRequired").value_or(false);
|
||||||
|
config.proxyUsername = getStringOpt("proxyUsername");
|
||||||
|
config.proxyPass = getStringOpt("proxyPass");
|
||||||
|
if(config.proxyAuthRequired) {
|
||||||
|
if(not config.proxyUsername) {
|
||||||
|
log(LogLevel::Error, "Proxy auth is required but proxy proxy username is not set");
|
||||||
|
return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration};
|
||||||
|
}
|
||||||
|
if(not config.proxyPass) {
|
||||||
|
log(LogLevel::Error, "Proxy is enabled but proxy password is not set,");
|
||||||
|
return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return configuration;
|
config.proxyPort = getInt("proxyPort").value_or(8080);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::pmr::memory_resource * memoryResource;
|
||||||
|
std::pmr::map< std::pmr::string, std::pmr::string > keyValues{memoryResource};
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConfigurationFactory {
|
||||||
|
public:
|
||||||
|
ConfigurationFactory() = default;
|
||||||
|
|
||||||
|
std::optional< Configuration > systemConfig() {
|
||||||
|
std::optional< Configuration > conf{Configuration{}};
|
||||||
|
ConfigurationReader reader;
|
||||||
|
reader.loadFromFile("/etc/rublon.config");
|
||||||
|
if(auto ok = reader.applyTo(conf.value()); not ok.has_value()){
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return conf.value();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace rublon
|
} // namespace rublon
|
||||||
|
|||||||
@ -172,7 +172,8 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
|
|||||||
|
|
||||||
signRequest(request);
|
signRequest(request);
|
||||||
std::pmr::string uri{&memoryResource};
|
std::pmr::string uri{&memoryResource};
|
||||||
uri += _config.apiServer.c_str();
|
uri.reserve(_config.apiServer.size() + path.size() + 1);
|
||||||
|
uri += _config.apiServer;
|
||||||
uri += path.data();
|
uri += path.data();
|
||||||
|
|
||||||
return http
|
return http
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "rublon/memory.hpp"
|
#include "rublon/memory.hpp"
|
||||||
|
#include <cstddef>
|
||||||
#include <rublon/error.hpp>
|
#include <rublon/error.hpp>
|
||||||
#include <rublon/utils.hpp>
|
#include <rublon/utils.hpp>
|
||||||
#include <rublon/configuration.hpp>
|
#include <rublon/configuration.hpp>
|
||||||
@ -11,6 +12,7 @@
|
|||||||
|
|
||||||
namespace rublon {
|
namespace rublon {
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
size_t WriteMemoryCallback(void * contents, size_t size, size_t nmemb, void * userp) {
|
size_t WriteMemoryCallback(void * contents, size_t size, size_t nmemb, void * userp) {
|
||||||
const size_t realsize = size * nmemb;
|
const size_t realsize = size * nmemb;
|
||||||
@ -60,7 +62,7 @@ class CURL {
|
|||||||
tl::expected< std::reference_wrapper< Response >, ConnectionError >
|
tl::expected< std::reference_wrapper< Response >, ConnectionError >
|
||||||
request(std::string_view uri, const Request & request, Response & response) const {
|
request(std::string_view uri, const Request & request, Response & response) const {
|
||||||
using namespace memory::literals;
|
using namespace memory::literals;
|
||||||
memory::Monotonic_16k_HeapResource memoryResource;
|
memory::Monotonic_8k_HeapResource memoryResource;
|
||||||
|
|
||||||
std::pmr::string response_data{&memoryResource};
|
std::pmr::string response_data{&memoryResource};
|
||||||
response_data.reserve(4_kB);
|
response_data.reserve(4_kB);
|
||||||
@ -73,16 +75,20 @@ class CURL {
|
|||||||
|
|
||||||
// Optional: Build full proxy URL if proxy is enabled
|
// Optional: Build full proxy URL if proxy is enabled
|
||||||
if (_config.proxyEnabled) {
|
if (_config.proxyEnabled) {
|
||||||
std::pmr::string proxyUrl{&memoryResource};
|
// configuration reader check if proxy has needed fields
|
||||||
proxyUrl.reserve(4_kB);
|
assert(_config.proxyType.has_value());
|
||||||
|
assert(_config.proxyServer.has_value());
|
||||||
|
|
||||||
|
std::pmr::string proxyUrl{&memoryResource};
|
||||||
|
proxyUrl.reserve(conservative_estimate(_config.proxyType, _config.proxyServer, _config.proxyPort) + 10);
|
||||||
|
|
||||||
if (_config.proxyType == "http" || _config.proxyType == "https" || _config.proxyType == "socks4" || _config.proxyType == "socks5") {
|
if (_config.proxyType == "http" || _config.proxyType == "https" || _config.proxyType == "socks4" || _config.proxyType == "socks5") {
|
||||||
proxyUrl = _config.proxyType.c_str();
|
proxyUrl = *_config.proxyType;
|
||||||
proxyUrl += "://";
|
proxyUrl += "://";
|
||||||
proxyUrl += _config.proxyServer.c_str();
|
proxyUrl += *_config.proxyServer;
|
||||||
if (_config.proxyPort > 0) {
|
if (_config.proxyPort > 0) {
|
||||||
proxyUrl += ":";
|
proxyUrl += ":";
|
||||||
proxyUrl += std::to_string(_config.proxyPort);
|
proxyUrl += std::to_string(*_config.proxyPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_easy_setopt(curl.get(), CURLOPT_PROXY, proxyUrl.c_str());
|
curl_easy_setopt(curl.get(), CURLOPT_PROXY, proxyUrl.c_str());
|
||||||
@ -96,12 +102,19 @@ class CURL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_config.proxyAuthRequired) {
|
if (_config.proxyAuthRequired) {
|
||||||
|
assert(_config.proxyUsername.has_value());
|
||||||
|
assert(_config.proxyPass.has_value());
|
||||||
|
|
||||||
std::pmr::string proxyAuth{&memoryResource};
|
std::pmr::string proxyAuth{&memoryResource};
|
||||||
proxyAuth.reserve(1_kB);
|
proxyAuth.reserve(conservative_estimate(_config.proxyUsername->size() + _config.proxyPass->size()));
|
||||||
_config.proxyUsername.c_str();
|
|
||||||
proxyAuth += ":";
|
proxyAuth += *_config.proxyUsername;
|
||||||
proxyAuth += _config.proxyPass.c_str();
|
if(_config.proxyPass->size()) {
|
||||||
|
// can proxy have name but no pass?
|
||||||
|
proxyAuth += ":";
|
||||||
|
proxyAuth += *_config.proxyPass;
|
||||||
|
}
|
||||||
|
|
||||||
curl_easy_setopt(curl.get(), CURLOPT_PROXYUSERPWD, proxyAuth.c_str());
|
curl_easy_setopt(curl.get(), CURLOPT_PROXYUSERPWD, proxyAuth.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,12 +15,14 @@ class ConfigurationError {
|
|||||||
RequiredValueNotFound,
|
RequiredValueNotFound,
|
||||||
BadFailMode,
|
BadFailMode,
|
||||||
BadInput,
|
BadInput,
|
||||||
|
BadConfiguration,
|
||||||
Empty
|
Empty
|
||||||
};
|
};
|
||||||
constexpr static auto errorClassPrettyName = make_array< std::string_view >( //
|
constexpr static auto errorClassPrettyName = make_array< std::string_view >( //
|
||||||
"RequiredValueNotFound",
|
"RequiredValueNotFound",
|
||||||
"BadFailMode",
|
"BadFailMode",
|
||||||
"BadInput",
|
"BadInput",
|
||||||
|
"BadConfiguration",
|
||||||
"Empty");
|
"Empty");
|
||||||
constexpr static auto prettyName = "Configurtion Error";
|
constexpr static auto prettyName = "Configurtion Error";
|
||||||
|
|
||||||
|
|||||||
@ -4,16 +4,17 @@
|
|||||||
#include <rublon/configuration.hpp>
|
#include <rublon/configuration.hpp>
|
||||||
#include <rublon/json.hpp>
|
#include <rublon/json.hpp>
|
||||||
#include <rublon/method/method_select.hpp>
|
#include <rublon/method/method_select.hpp>
|
||||||
|
#include <string_view>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
|
|
||||||
namespace rublon {
|
namespace rublon {
|
||||||
class Finish : public AuthenticationStep {
|
class Finish : public AuthenticationStep {
|
||||||
const char * apiPath = "/api/transaction/credentials";
|
const char * apiPath = "/api/transaction/credentials";
|
||||||
const std::string _accessToken;
|
const std::string_view _accessToken; //
|
||||||
|
|
||||||
void addAccessToken(Document & coreRequest) const {
|
void addAccessToken(Document & coreRequest) const {
|
||||||
auto & alloc = coreRequest.GetAllocator();
|
auto & alloc = coreRequest.GetAllocator();
|
||||||
coreRequest.AddMember("accessToken", Value{_accessToken.c_str(), alloc}, alloc);
|
coreRequest.AddMember("accessToken", Value{_accessToken.data(), alloc}, alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
tl::expected< bool, Error > returnOk(const Document & /*coreResponse*/) const {
|
tl::expected< bool, Error > returnOk(const Document & /*coreResponse*/) const {
|
||||||
@ -23,7 +24,7 @@ class Finish : public AuthenticationStep {
|
|||||||
public:
|
public:
|
||||||
const char * name = "Finalization";
|
const char * name = "Finalization";
|
||||||
|
|
||||||
Finish(Session & session, std::string accessToken) : AuthenticationStep(session), _accessToken{std::move(accessToken)} {}
|
Finish(Session & session, std::string_view accessToken) : AuthenticationStep(session), _accessToken{accessToken} {}
|
||||||
|
|
||||||
template < typename Hander_t >
|
template < typename Hander_t >
|
||||||
tl::expected< bool, Error > handle(const CoreHandlerInterface< Hander_t > & coreHandler) const {
|
tl::expected< bool, Error > handle(const CoreHandlerInterface< Hander_t > & coreHandler) const {
|
||||||
|
|||||||
@ -43,12 +43,11 @@ class Init : public AuthenticationStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void addParams(Document & coreRequest, const Pam_t & pam) const {
|
void addParams(Document & coreRequest, const Pam_t & pam) const {
|
||||||
memory::MonotonicStackResource< 1024 > stackResource;
|
memory::MonotonicStackResource< 1024 > memoryResource;
|
||||||
std::pmr::string releaseInfo{&stackResource};
|
|
||||||
auto & alloc = coreRequest.GetAllocator();
|
auto & alloc = coreRequest.GetAllocator();
|
||||||
|
|
||||||
const auto os = details::osName(&stackResource);
|
const auto os = details::osName(&memoryResource);
|
||||||
const auto host = details::hostname(&stackResource);
|
const auto host = details::hostname(&memoryResource);
|
||||||
|
|
||||||
if(os == "unknown") {
|
if(os == "unknown") {
|
||||||
log(LogLevel::Warning, "No OS information available");
|
log(LogLevel::Warning, "No OS information available");
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <rublon/memory.hpp>
|
#include <rublon/memory.hpp>
|
||||||
#include <rublon/static_string.hpp>
|
#include <rublon/static_string.hpp>
|
||||||
#include <rublon/stdlib.hpp>
|
#include <rublon/stdlib.hpp>
|
||||||
@ -158,6 +159,14 @@ namespace conv {
|
|||||||
return tl::make_unexpected(Error::OutOfRange);
|
return tl::make_unexpected(Error::OutOfRange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::optional< std::uint32_t> to_uint32opt(std::string_view userinput) noexcept {
|
||||||
|
try {
|
||||||
|
return std::stoi(userinput.data());
|
||||||
|
} catch(...) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace conv
|
} // namespace conv
|
||||||
|
|
||||||
namespace details {
|
namespace details {
|
||||||
@ -176,6 +185,23 @@ namespace details {
|
|||||||
static inline std::string_view trim(std::string_view s) {
|
static inline std::string_view trim(std::string_view s) {
|
||||||
return ltrim(rtrim(s));
|
return ltrim(rtrim(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename StrT>
|
||||||
|
void trimInPlace(StrT & s) {
|
||||||
|
// Remove leading whitespace
|
||||||
|
size_t start = 0;
|
||||||
|
while(start < s.size() && isspace(static_cast< unsigned char >(s[start])))
|
||||||
|
++start;
|
||||||
|
|
||||||
|
// Remove trailing whitespace
|
||||||
|
size_t end = s.size();
|
||||||
|
while(end > start && isspace(static_cast< unsigned char >(s[end - 1])))
|
||||||
|
--end;
|
||||||
|
|
||||||
|
if(start > 0 || end < s.size()) {
|
||||||
|
s = s.substr(start, end - start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template < typename Headers >
|
template < typename Headers >
|
||||||
inline void headers(std::string_view data, Headers & headers) {
|
inline void headers(std::string_view data, Headers & headers) {
|
||||||
@ -197,7 +223,7 @@ namespace details {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::pmr::string hostname(std::pmr::memory_resource * mr) {
|
std::pmr::string hostname(std::pmr::memory_resource * mr) {
|
||||||
std::pmr::string hostname{512, '\0', mr};
|
std::pmr::string hostname{2048, '\0', mr};
|
||||||
if(gethostname(hostname.data(), hostname.size()) != 0) {
|
if(gethostname(hostname.data(), hostname.size()) != 0) {
|
||||||
log(LogLevel::Warning, "Hostname is not available");
|
log(LogLevel::Warning, "Hostname is not available");
|
||||||
return "";
|
return "";
|
||||||
@ -207,18 +233,18 @@ namespace details {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::pmr::string osName(std::pmr::memory_resource * mr) {
|
std::pmr::string osName(std::pmr::memory_resource * mr) {
|
||||||
memory::MonotonicStackResource< 8 * 1024 > stackResource;
|
memory::Monotonic_8k_HeapResource memoryResource;
|
||||||
|
|
||||||
std::ifstream file(std::filesystem::path{"/etc/os-release"});
|
std::ifstream file(std::filesystem::path{"/etc/os-release"});
|
||||||
if(not file.good())
|
if(not file.good())
|
||||||
return {"unknown", mr};
|
return {"unknown", mr};
|
||||||
|
|
||||||
std::pmr::string line{&stackResource};
|
std::pmr::string line{&memoryResource};
|
||||||
|
std::pmr::string _key{&memoryResource};
|
||||||
|
std::pmr::string _value{&memoryResource};
|
||||||
line.reserve(100);
|
line.reserve(100);
|
||||||
|
|
||||||
while(std::getline(file, line)) {
|
while(std::getline(file, line)) {
|
||||||
std::pmr::string _key{&stackResource};
|
|
||||||
std::pmr::string _value{&stackResource};
|
|
||||||
|
|
||||||
if(!line.length())
|
if(!line.length())
|
||||||
continue;
|
continue;
|
||||||
@ -266,4 +292,29 @@ constexpr std::array< Out, sizeof...(Types) > make_array(Types... names) {
|
|||||||
return {std::forward< Types >(names)...};
|
return {std::forward< Types >(names)...};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> std::size_t size_buffer(const T &item) {
|
||||||
|
using U = std::decay_t<T>;
|
||||||
|
if constexpr (std::is_same_v<U, const char *>) {
|
||||||
|
return strlen(item);
|
||||||
|
} else if constexpr(std::is_same_v< U, std::pmr::string > || std::is_same_v< U, std::string >) {
|
||||||
|
return item.size();
|
||||||
|
} else if constexpr(std::is_integral_v< U > || std::is_floating_point_v< U >) {
|
||||||
|
return std::numeric_limits<U>::digits;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> std::size_t size_buffer(const std::optional<T> &item) {
|
||||||
|
if(item.has_value())
|
||||||
|
return size_buffer(*item);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// min + 10%
|
||||||
|
template < typename... Args >
|
||||||
|
std::size_t conservative_estimate(const Args &... args) {
|
||||||
|
auto min = (size_buffer(args) + ...);
|
||||||
|
return min + min * 10 / 100;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace rublon
|
} // namespace rublon
|
||||||
|
|||||||
@ -44,10 +44,8 @@ class WebSocket {
|
|||||||
RublonEventData * currentEvent{nullptr};
|
RublonEventData * currentEvent{nullptr};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WebSocket(const Configuration & config) : _config{config}, urlv{_config.get().apiServer.c_str(), _config.get().apiServer.size()} {
|
WebSocket(const Configuration & config) : _config{config}, urlv{_config.get().apiServer} {
|
||||||
using namespace memory::literals;
|
const auto & cfg = _config.get(); // only a alias to not use _config.get() all the time
|
||||||
memory::Monotonic_8k_HeapResource memoryResource;
|
|
||||||
const auto & cfg = _config.get();
|
|
||||||
|
|
||||||
auto lws_log_emit = [](int level, const char * line) {
|
auto lws_log_emit = [](int level, const char * line) {
|
||||||
LogLevel rlevel{};
|
LogLevel rlevel{};
|
||||||
@ -77,23 +75,27 @@ class WebSocket {
|
|||||||
context = lws_create_context(&info);
|
context = lws_create_context(&info);
|
||||||
|
|
||||||
if(cfg.proxyEnabled && (cfg.proxyType == "http" || cfg.proxyType == "https")) {
|
if(cfg.proxyEnabled && (cfg.proxyType == "http" || cfg.proxyType == "https")) {
|
||||||
|
assert(cfg.proxyType.has_value());
|
||||||
|
assert(cfg.proxyServer.has_value());
|
||||||
|
|
||||||
|
memory::Monotonic_8k_HeapResource memoryResource;
|
||||||
std::pmr::string proxyUrl{&memoryResource};
|
std::pmr::string proxyUrl{&memoryResource};
|
||||||
proxyUrl.reserve(4_kB);
|
proxyUrl.reserve(conservative_estimate(cfg.proxyUsername, cfg.proxyPass, cfg.proxyServer, cfg.proxyPort) + 10);
|
||||||
|
|
||||||
proxyUrl += cfg.proxyType.data();
|
proxyUrl += cfg.proxyType->data();
|
||||||
proxyUrl += "://";
|
proxyUrl += "://";
|
||||||
|
|
||||||
if(cfg.proxyAuthRequired) {
|
if(cfg.proxyAuthRequired) {
|
||||||
proxyUrl += cfg.proxyUsername.c_str();
|
proxyUrl += *cfg.proxyUsername;
|
||||||
proxyUrl += ":";
|
proxyUrl += ":";
|
||||||
proxyUrl += cfg.proxyPass.c_str();
|
proxyUrl += *cfg.proxyPass;
|
||||||
proxyUrl += "@";
|
proxyUrl += "@";
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyUrl += cfg.proxyServer.c_str();
|
proxyUrl += *cfg.proxyServer;
|
||||||
if(cfg.proxyPort > 0) {
|
if(cfg.proxyPort > 0) {
|
||||||
proxyUrl += ":";
|
proxyUrl += ":";
|
||||||
proxyUrl += std::to_string(cfg.proxyPort);
|
proxyUrl += std::to_string(*cfg.proxyPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set environment variable for libwebsockets to pick up
|
// Set environment variable for libwebsockets to pick up
|
||||||
@ -101,7 +103,6 @@ class WebSocket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::string_view prefix = "https://";
|
const std::string_view prefix = "https://";
|
||||||
|
|
||||||
if(urlv.substr(0, prefix.size()) == prefix)
|
if(urlv.substr(0, prefix.size()) == prefix)
|
||||||
urlv.remove_prefix(prefix.size());
|
urlv.remove_prefix(prefix.size());
|
||||||
|
|
||||||
|
|||||||
@ -83,9 +83,7 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu
|
|||||||
|
|
||||||
auto finalizeTransaction = [&](const AuthenticationStatus & status) mutable -> tl::expected< AuthenticationStatus, Error > {
|
auto finalizeTransaction = [&](const AuthenticationStatus & status) mutable -> tl::expected< AuthenticationStatus, Error > {
|
||||||
if(status.userAuthorized()) {
|
if(status.userAuthorized()) {
|
||||||
auto tok = std::string{status.accessToken().data()};
|
Finish{session.value(), status.accessToken()}.handle(CH);
|
||||||
Finish finish{session.value(), std::move(tok)};
|
|
||||||
finish.handle(CH);
|
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
};
|
};
|
||||||
@ -101,7 +99,7 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu
|
|||||||
{
|
{
|
||||||
CheckApplication ca;
|
CheckApplication ca;
|
||||||
const auto & config = session.value().config();
|
const auto & config = session.value().config();
|
||||||
const auto ret = ca.call(CH, {config.systemToken.data(), config.systemToken.size()}).or_else(mapError);
|
const auto ret = ca.call(CH, config.systemToken).or_else(mapError);
|
||||||
if(not ret.has_value()) {
|
if(not ret.has_value()) {
|
||||||
log(LogLevel::Error, "Check Application step failed, check configration");
|
log(LogLevel::Error, "Check Application step failed, check configration");
|
||||||
return PAM_MAXTRIES;
|
return PAM_MAXTRIES;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user