Fix access to unknown object in json
This commit is contained in:
parent
626bcbd289
commit
db418ef01d
@ -16,6 +16,14 @@
|
|||||||
|
|
||||||
namespace rublon {
|
namespace rublon {
|
||||||
class ConfigurationFactory;
|
class ConfigurationFactory;
|
||||||
|
|
||||||
|
class ConfigurationError {
|
||||||
|
public:
|
||||||
|
enum class Cause{NoDefaultValue};
|
||||||
|
const char * parameterName;
|
||||||
|
const char * cause;
|
||||||
|
};
|
||||||
|
|
||||||
class Configuration {
|
class Configuration {
|
||||||
public:
|
public:
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
@ -27,10 +35,112 @@ class Configuration {
|
|||||||
bool logging;
|
bool logging;
|
||||||
bool autopushPrompt;
|
bool autopushPrompt;
|
||||||
bool bypass;
|
bool bypass;
|
||||||
bool offlineBypass;
|
bool offlineBypas;
|
||||||
} parameters;
|
} 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 {
|
class ConfigurationFactory {
|
||||||
public:
|
public:
|
||||||
ConfigurationFactory() = default;
|
ConfigurationFactory() = default;
|
||||||
@ -47,7 +157,11 @@ class ConfigurationFactory {
|
|||||||
std::pmr::string line{&stackResource};
|
std::pmr::string line{&stackResource};
|
||||||
line.reserve(100);
|
line.reserve(100);
|
||||||
std::pmr::map< std::pmr::string, std::pmr::string > parameters{&stackResource};
|
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{&stackResource};
|
||||||
std::pmr::string value{&stackResource};
|
std::pmr::string value{&stackResource};
|
||||||
@ -64,44 +178,10 @@ class ConfigurationFactory {
|
|||||||
|
|
||||||
parameters[std::move(key)] = std::move(value);
|
parameters[std::move(key)] = std::move(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto saveStr = [&](auto member) { //
|
|
||||||
return [member](Params * params, std::string_view value) { params->*member = value; };
|
|
||||||
};
|
|
||||||
|
|
||||||
auto saveInt = [](auto member) { //
|
|
||||||
return [member](Params * params, std::string_view value) { params->*member = std::stoi(value.data()); };
|
|
||||||
};
|
|
||||||
|
|
||||||
auto saveBool = [](auto member) {
|
|
||||||
return [member](Params * params, std::string_view value) { params->*member = details::to_bool(value); };
|
|
||||||
};
|
|
||||||
|
|
||||||
auto saveBypass = [](auto member) {
|
for(const auto &entry : configurationVariables){
|
||||||
return [member](Params * params, std::string_view value) { params->*member = (value == "bypas"); };
|
if(not entry.read(&configValues, readParameterByName(entry.name)))
|
||||||
};
|
return std::nullopt;
|
||||||
|
|
||||||
std::tuple< const char *, std::function< void(Params *, const char *) >, const char * > checks[]{//
|
|
||||||
{"logging", saveBool(&Params::logging), "true"},
|
|
||||||
{"systemToken", saveStr(&Params::systemToken), nullptr},
|
|
||||||
{"secretKey", saveStr(&Params::secretKey), nullptr},
|
|
||||||
{"rublonApiServer", saveStr(&Params::apiServer), nullptr},
|
|
||||||
{"prompt", saveInt(&Params::prompt), "1"},
|
|
||||||
{"enablePasswdEmail", saveBool(&Params::enablePasswdEmail), "true"},
|
|
||||||
{"autopushPrompt", saveBool(&Params::autopushPrompt), "false"},
|
|
||||||
{"failMode", saveBypass(&Params::bypass), "bypas"}/*,
|
|
||||||
{"offlineBypass", saveBool(&Params::bypass), "true"}*/};
|
|
||||||
|
|
||||||
for(const auto & [key, set_func, default_value] : checks) {
|
|
||||||
if(auto it = parameters.find(key); it != parameters.end()) {
|
|
||||||
set_func(&configValues, it->second.data());
|
|
||||||
} else {
|
|
||||||
if(default_value) {
|
|
||||||
/// TODO exit??
|
|
||||||
} else { // not required
|
|
||||||
set_func(&configValues, default_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Configuration{std::move(configValues)};
|
return Configuration{std::move(configValues)};
|
||||||
|
|||||||
@ -60,7 +60,7 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
|
|||||||
return tl::unexpected{CoreHandlerError{CoreHandlerError::BrokenData}};
|
return tl::unexpected{CoreHandlerError{CoreHandlerError::BrokenData}};
|
||||||
}
|
}
|
||||||
|
|
||||||
if(resp["result"].HasMember("exception")) {
|
if(resp.HasMember("result") and resp["result"].IsObject() and resp["result"].HasMember("exception")) {
|
||||||
const auto & exception = resp["result"]["exception"].GetString();
|
const auto & exception = resp["result"]["exception"].GetString();
|
||||||
log(LogLevel::Error, "rublon Core exception %s", exception);
|
log(LogLevel::Error, "rublon Core exception %s", exception);
|
||||||
return handleCoreException(exception);
|
return handleCoreException(exception);
|
||||||
|
|||||||
@ -36,7 +36,7 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu
|
|||||||
|
|
||||||
auto rublonConfig = ConfigurationFactory{}.systemConfig();
|
auto rublonConfig = ConfigurationFactory{}.systemConfig();
|
||||||
if(not rublonConfig.has_value()) {
|
if(not rublonConfig.has_value()) {
|
||||||
pam.print("Rublon configuration not exists");
|
pam.print("Rublon configuration not exists or is invalid (check logs)");
|
||||||
pam.print("Please create /etc/rublon.config file");
|
pam.print("Please create /etc/rublon.config file");
|
||||||
return PAM_SUCCESS;
|
return PAM_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ class CoreHandlerMock : public rublon::CoreHandlerInterface< CoreHandlerMock > {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
tl::expected< rublon::Document, rublon::Error >
|
tl::expected< rublon::Document, rublon::Error >
|
||||||
request(rublon::RapidJSONPMRAlloc &alloc, std::string_view path, const rublon::Document & req) const {
|
request(rublon::RapidJSONPMRAlloc &, std::string_view path, const rublon::Document & req) const {
|
||||||
// const rublon::Document &doc = request(path, req).value();
|
// const rublon::Document &doc = request(path, req).value();
|
||||||
// rublon::Document ret{&alloc};
|
// rublon::Document ret{&alloc};
|
||||||
// ret.CopyFrom(doc, alloc);
|
// ret.CopyFrom(doc, alloc);
|
||||||
|
|||||||
@ -69,6 +69,12 @@ TEST_F(CoreHandlerTests, onHttpProblemsAccessShouldBeDeniedByDefault) {
|
|||||||
AllOf(Not(HasValue()), Unexpected(PamDeny{})));
|
AllOf(Not(HasValue()), Unexpected(PamDeny{})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(CoreHandlerTests, onSuccessfullMessageCoreShouldByFine){
|
||||||
|
EXPECT_CALL(http, request(_, _)).WillOnce(Return(RawHttpResponse{_res}.authenticationSuccessfullMessage()));
|
||||||
|
EXPECT_THAT(sut.request("/path", doc), //
|
||||||
|
AllOf(HasValue()));
|
||||||
|
}
|
||||||
|
|
||||||
class CoreHandlerWithBypassTests : public testing::Test {
|
class CoreHandlerWithBypassTests : public testing::Test {
|
||||||
public:
|
public:
|
||||||
CoreHandlerWithBypassTests() : sut{confBypass}, http{sut._http()} {
|
CoreHandlerWithBypassTests() : sut{confBypass}, http{sut._http()} {
|
||||||
|
|||||||
@ -133,6 +133,15 @@ class CodeVerificationResponse {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AuthenticationOkResponse {
|
||||||
|
static constexpr const char * authOk = R"json({"status":"OK","result":true})json";
|
||||||
|
|
||||||
|
public:
|
||||||
|
static std::string generate(const ResponseMockOptions &) {
|
||||||
|
return authOk;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template < typename Generator >
|
template < typename Generator >
|
||||||
class ResponseBase {
|
class ResponseBase {
|
||||||
public:
|
public:
|
||||||
@ -151,6 +160,11 @@ class ResponseBase {
|
|||||||
return static_cast< Generator & >(*this);
|
return static_cast< Generator & >(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Generator & authenticationSuccessfullMessage() {
|
||||||
|
_coreGenerator = AuthenticationOkResponse{};
|
||||||
|
return static_cast< Generator & >(*this);
|
||||||
|
}
|
||||||
|
|
||||||
Generator & withBrokenBody() {
|
Generator & withBrokenBody() {
|
||||||
options.generateBrokenBody = true;
|
options.generateBrokenBody = true;
|
||||||
return static_cast< Generator & >(*this);
|
return static_cast< Generator & >(*this);
|
||||||
@ -205,7 +219,7 @@ class ResponseBase {
|
|||||||
return rublon::Error{options.error};
|
return rublon::Error{options.error};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::variant< InitCoreResponse, MethodSelectCoreResponse, CodeVerificationResponse > _coreGenerator;
|
std::variant< InitCoreResponse, MethodSelectCoreResponse, CodeVerificationResponse, AuthenticationOkResponse > _coreGenerator;
|
||||||
ResponseMockOptions options;
|
ResponseMockOptions options;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user