bwi/v2.3.0 #29

Merged
bartoszek merged 38 commits from bwi/v2.3.0 into main 2025-09-11 08:32:43 +00:00
9 changed files with 274 additions and 203 deletions
Showing only changes of commit e5cc1f619e - Show all commits

View File

@ -1,14 +1,14 @@
#pragma once
#include <cctype>
#include <rublon/memory.hpp>
#include <rublon/error.hpp>
#include <rublon/static_string.hpp>
#include <rublon/utils.hpp>
#include <type_traits>
template < typename T >
constexpr bool is_static_string_v = std::is_base_of_v< rublon::details::StaticStringBase, T >;
static_assert(is_static_string_v< rublon::StaticString< 32 > >);
#include <fstream>
#include <optional>
#include <string>
namespace rublon {
class ConfigurationFactory;
@ -16,11 +16,16 @@ class ConfigurationFactory;
enum class FailMode { bypass, deny };
class Configuration {
private:
std::pmr::memory_resource * memoryResource;
public:
Configuration() : memoryResource{memory::default_resource()} {}
// change to StaticString
StaticString< 32 > systemToken{};
StaticString< 32 > secretKey{};
StaticString< 4096 > apiServer{};
std::pmr::string systemToken{memoryResource};
std::pmr::string secretKey{memoryResource};
std::pmr::string apiServer{memoryResource};
int prompt{};
bool enablePasswdEmail{};
@ -29,168 +34,36 @@ class Configuration {
FailMode failMode{};
bool nonInteractiveMode{};
StaticString< 8 > proxyType{};
StaticString< 4096 > proxyServer{};
StaticString< 256 > proxyUsername{};
StaticString< 256 > proxyPass{};
int proxyPort{};
bool proxyAuthRequired{};
bool proxyEnabled{};
std::optional< std::pmr::string > proxyType{memoryResource};
std::optional< std::pmr::string > proxyServer{memoryResource};
std::optional< std::pmr::string > proxyUsername{memoryResource};
std::optional< std::pmr::string > proxyPass{memoryResource};
std::optional< int > proxyPort{};
bool proxyAuthRequired{}; // defaulted
bool proxyEnabled{}; // defaulted
};
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 < 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 {
class ConfigurationReader {
public:
ConfigurationFactory() = default;
ConfigurationReader(std::pmr::memory_resource * memResource = memory::default_resource()) : memoryResource(memResource) {}
std::optional< Configuration > systemConfig() {
memory::MonotonicStackResource< 16 * 1024 > stackResource;
Configuration configuration{};
std::ifstream file(std::filesystem::path{"/etc/rublon.config"});
// Load config from file path
bool loadFromFile(const std::string & filepath) {
using namespace memory::literals;
memory::MonotonicStackResource< 8_kB > stackResource;
std::ifstream file(filepath);
if(not file.good())
return std::nullopt;
return false;
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;
};
line.reserve(8000); // allocate full stack to line
while(std::getline(file, line)) {
std::pmr::string key{&stackResource};
std::pmr::string value{&stackResource};
std::pmr::string key{memoryResource};
std::pmr::string value{memoryResource};
details::trimInPlace(line);
if(!line.length())
continue;
@ -201,15 +74,147 @@ class ConfigurationFactory {
key = line.substr(0, posEqual);
value = line.substr(posEqual + 1);
parameters[std::move(key)] = std::move(value);
keyValues[std::move(key)] = std::move(value);
}
for(const auto & entry : configurationVariables) {
if(not entry.read(&configuration, readParameterByName(entry.name)))
return true;
}
// 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 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

View File

@ -172,7 +172,8 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
signRequest(request);
std::pmr::string uri{&memoryResource};
uri += _config.apiServer.c_str();
uri.reserve(_config.apiServer.size() + path.size() + 1);
uri += _config.apiServer;
uri += path.data();
return http

View File

@ -1,6 +1,7 @@
#pragma once
#include "rublon/memory.hpp"
#include <cstddef>
#include <rublon/error.hpp>
#include <rublon/utils.hpp>
#include <rublon/configuration.hpp>
@ -11,6 +12,7 @@
namespace rublon {
namespace {
size_t WriteMemoryCallback(void * contents, size_t size, size_t nmemb, void * userp) {
const size_t realsize = size * nmemb;
@ -60,7 +62,7 @@ class CURL {
tl::expected< std::reference_wrapper< Response >, ConnectionError >
request(std::string_view uri, const Request & request, Response & response) const {
using namespace memory::literals;
memory::Monotonic_16k_HeapResource memoryResource;
memory::Monotonic_8k_HeapResource memoryResource;
std::pmr::string response_data{&memoryResource};
response_data.reserve(4_kB);
@ -73,16 +75,20 @@ class CURL {
// Optional: Build full proxy URL if proxy is enabled
if (_config.proxyEnabled) {
std::pmr::string proxyUrl{&memoryResource};
proxyUrl.reserve(4_kB);
// configuration reader check if proxy has needed fields
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") {
proxyUrl = _config.proxyType.c_str();
proxyUrl = *_config.proxyType;
proxyUrl += "://";
proxyUrl += _config.proxyServer.c_str();
proxyUrl += *_config.proxyServer;
if (_config.proxyPort > 0) {
proxyUrl += ":";
proxyUrl += std::to_string(_config.proxyPort);
proxyUrl += std::to_string(*_config.proxyPort);
}
curl_easy_setopt(curl.get(), CURLOPT_PROXY, proxyUrl.c_str());
@ -96,12 +102,19 @@ class CURL {
}
if (_config.proxyAuthRequired) {
assert(_config.proxyUsername.has_value());
assert(_config.proxyPass.has_value());
std::pmr::string proxyAuth{&memoryResource};
proxyAuth.reserve(1_kB);
_config.proxyUsername.c_str();
proxyAuth += ":";
proxyAuth += _config.proxyPass.c_str();
proxyAuth.reserve(conservative_estimate(_config.proxyUsername->size() + _config.proxyPass->size()));
proxyAuth += *_config.proxyUsername;
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());
}
}

View File

@ -15,12 +15,14 @@ class ConfigurationError {
RequiredValueNotFound,
BadFailMode,
BadInput,
BadConfiguration,
Empty
};
constexpr static auto errorClassPrettyName = make_array< std::string_view >( //
"RequiredValueNotFound",
"BadFailMode",
"BadInput",
"BadConfiguration",
"Empty");
constexpr static auto prettyName = "Configurtion Error";

View File

@ -4,16 +4,17 @@
#include <rublon/configuration.hpp>
#include <rublon/json.hpp>
#include <rublon/method/method_select.hpp>
#include <string_view>
#include <sys/utsname.h>
namespace rublon {
class Finish : public AuthenticationStep {
const char * apiPath = "/api/transaction/credentials";
const std::string _accessToken;
const std::string_view _accessToken; //
void addAccessToken(Document & coreRequest) const {
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 {
@ -23,7 +24,7 @@ class Finish : public AuthenticationStep {
public:
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 >
tl::expected< bool, Error > handle(const CoreHandlerInterface< Hander_t > & coreHandler) const {

View File

@ -43,12 +43,11 @@ class Init : public AuthenticationStep {
}
void addParams(Document & coreRequest, const Pam_t & pam) const {
memory::MonotonicStackResource< 1024 > stackResource;
std::pmr::string releaseInfo{&stackResource};
memory::MonotonicStackResource< 1024 > memoryResource;
auto & alloc = coreRequest.GetAllocator();
const auto os = details::osName(&stackResource);
const auto host = details::hostname(&stackResource);
const auto os = details::osName(&memoryResource);
const auto host = details::hostname(&memoryResource);
if(os == "unknown") {
log(LogLevel::Warning, "No OS information available");

View File

@ -1,5 +1,6 @@
#pragma once
#include <optional>
#include <rublon/memory.hpp>
#include <rublon/static_string.hpp>
#include <rublon/stdlib.hpp>
@ -158,6 +159,14 @@ namespace conv {
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 details {
@ -176,6 +185,23 @@ namespace details {
static inline std::string_view trim(std::string_view 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 >
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{512, '\0', mr};
std::pmr::string hostname{2048, '\0', mr};
if(gethostname(hostname.data(), hostname.size()) != 0) {
log(LogLevel::Warning, "Hostname is not available");
return "";
@ -207,18 +233,18 @@ namespace details {
}
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"});
if(not file.good())
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);
while(std::getline(file, line)) {
std::pmr::string _key{&stackResource};
std::pmr::string _value{&stackResource};
if(!line.length())
continue;
@ -266,4 +292,29 @@ constexpr std::array< Out, sizeof...(Types) > make_array(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

View File

@ -44,10 +44,8 @@ class WebSocket {
RublonEventData * currentEvent{nullptr};
public:
WebSocket(const Configuration & config) : _config{config}, urlv{_config.get().apiServer.c_str(), _config.get().apiServer.size()} {
using namespace memory::literals;
memory::Monotonic_8k_HeapResource memoryResource;
const auto & cfg = _config.get();
WebSocket(const Configuration & config) : _config{config}, urlv{_config.get().apiServer} {
const auto & cfg = _config.get(); // only a alias to not use _config.get() all the time
auto lws_log_emit = [](int level, const char * line) {
LogLevel rlevel{};
@ -77,23 +75,27 @@ class WebSocket {
context = lws_create_context(&info);
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};
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 += "://";
if(cfg.proxyAuthRequired) {
proxyUrl += cfg.proxyUsername.c_str();
proxyUrl += *cfg.proxyUsername;
proxyUrl += ":";
proxyUrl += cfg.proxyPass.c_str();
proxyUrl += *cfg.proxyPass;
proxyUrl += "@";
}
proxyUrl += cfg.proxyServer.c_str();
proxyUrl += *cfg.proxyServer;
if(cfg.proxyPort > 0) {
proxyUrl += ":";
proxyUrl += std::to_string(cfg.proxyPort);
proxyUrl += std::to_string(*cfg.proxyPort);
}
// Set environment variable for libwebsockets to pick up
@ -101,7 +103,6 @@ class WebSocket {
}
const std::string_view prefix = "https://";
if(urlv.substr(0, prefix.size()) == prefix)
urlv.remove_prefix(prefix.size());

View File

@ -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 > {
if(status.userAuthorized()) {
auto tok = std::string{status.accessToken().data()};
Finish finish{session.value(), std::move(tok)};
finish.handle(CH);
Finish{session.value(), status.accessToken()}.handle(CH);
}
return status;
};
@ -101,7 +99,7 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu
{
CheckApplication ca;
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()) {
log(LogLevel::Error, "Check Application step failed, check configration");
return PAM_MAXTRIES;