Fix access to unknown object in json

This commit is contained in:
Bartosz Wieczorek 2023-10-11 12:23:55 +02:00
parent 626bcbd289
commit db418ef01d
6 changed files with 143 additions and 43 deletions

View File

@ -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)};

View File

@ -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);

View File

@ -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;
} }

View File

@ -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);

View File

@ -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()} {

View File

@ -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;
}; };