Bwi/bugfix (#7)
* generate user enrolement message * Fix bugs found during testing
This commit is contained in:
parent
7715b6fb45
commit
c3127e8b58
@ -30,6 +30,10 @@ install(
|
||||
share/rublon
|
||||
COMPONENT
|
||||
PAM
|
||||
PERMISSIONS
|
||||
OWNER_READ
|
||||
OWNER_WRITE
|
||||
GROUP_READ
|
||||
)
|
||||
|
||||
if (${ENABLE_TESTS})
|
||||
|
||||
@ -1,15 +1,10 @@
|
||||
#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"
|
||||
@ -26,17 +21,15 @@ class ConfigurationError {
|
||||
|
||||
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;
|
||||
std::array< char, 33 > systemToken{};
|
||||
std::array< char, 33 > secretKey{};
|
||||
std::array< char, 300 > apiServer{};
|
||||
int prompt{};
|
||||
bool enablePasswdEmail{};
|
||||
bool logging{};
|
||||
bool autopushPrompt{};
|
||||
bool bypass{};
|
||||
bool offlineBypas{};
|
||||
};
|
||||
|
||||
namespace {
|
||||
@ -45,11 +38,24 @@ namespace {
|
||||
|
||||
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()};
|
||||
auto to(std::string_view arg) -> tl::expected< std::array<char, 33>, ConfigurationError > {
|
||||
assert(arg.size()<=(33-1));
|
||||
|
||||
std::array<char, 33> value{};
|
||||
std::memcpy(value.data(), arg.data(), arg.size());
|
||||
return value;
|
||||
}
|
||||
template <>
|
||||
auto to(std::string_view arg) -> tl::expected< std::array<char, 300>, ConfigurationError > {
|
||||
assert(arg.size()<=(300-1));
|
||||
|
||||
std::array<char, 300> value{};
|
||||
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);
|
||||
@ -67,15 +73,15 @@ struct Entry {
|
||||
using pType = decltype(member_ptr_t(member));
|
||||
|
||||
return [](const Entry * _this,
|
||||
Configuration::Parameters * params,
|
||||
Configuration * configuration,
|
||||
std::string_view userInput) -> tl::expected< Source, ConfigurationError > {
|
||||
const auto setDefaultValue = [&](const auto & value) -> tl::expected< Source, ConfigurationError > {
|
||||
params->*member = value;
|
||||
configuration->*member = value;
|
||||
return Source::DefaultValue;
|
||||
};
|
||||
|
||||
const auto saveValue = [&](const auto & value) -> tl::expected< Source, ConfigurationError > {
|
||||
params->*member = value;
|
||||
configuration->*member = value;
|
||||
return Source::UserInput;
|
||||
};
|
||||
|
||||
@ -97,10 +103,9 @@ struct Entry {
|
||||
|
||||
const char * name;
|
||||
const char * defaultValue;
|
||||
tl::expected< Source, ConfigurationError > (
|
||||
*_read)(const Entry * _this, Configuration::Parameters * params, std::string_view userInput);
|
||||
tl::expected< Source, ConfigurationError > (*_read)(const Entry * _this, Configuration * configuration, std::string_view userInput);
|
||||
|
||||
bool read(Configuration::Parameters * params, std::optional< std::string_view > userInput) const {
|
||||
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,
|
||||
@ -118,7 +123,7 @@ struct Entry {
|
||||
return tl::unexpected{error};
|
||||
};
|
||||
|
||||
return _read(this, params, userInput.value_or(emptyString)).and_then(logStored).or_else(logError).has_value();
|
||||
return _read(this, configuration, userInput.value_or(emptyString)).and_then(logStored).or_else(logError).has_value();
|
||||
}
|
||||
};
|
||||
|
||||
@ -127,16 +132,15 @@ 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")};
|
||||
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::offlineBypas >("failMode", "bypas")};
|
||||
|
||||
class ConfigurationFactory {
|
||||
public:
|
||||
@ -144,8 +148,7 @@ class ConfigurationFactory {
|
||||
|
||||
std::optional< Configuration > systemConfig() {
|
||||
memory::MonotonicStackResource< 8 * 1024 > stackResource;
|
||||
using Params = Configuration::Parameters;
|
||||
Params configValues;
|
||||
Configuration configuration{};
|
||||
|
||||
std::ifstream file(std::filesystem::path{"/etc/rublon.config"});
|
||||
if(not file.good())
|
||||
@ -177,11 +180,11 @@ class ConfigurationFactory {
|
||||
}
|
||||
|
||||
for(const auto & entry : configurationVariables) {
|
||||
if(not entry.read(&configValues, readParameterByName(entry.name)))
|
||||
if(not entry.read(&configuration, readParameterByName(entry.name)))
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return Configuration{std::move(configValues)};
|
||||
return configuration;
|
||||
}
|
||||
};
|
||||
} // namespace rublon
|
||||
|
||||
@ -3,15 +3,14 @@
|
||||
#include <memory_resource>
|
||||
#include <string>
|
||||
|
||||
#include "configuration.hpp"
|
||||
#include "curl.hpp"
|
||||
#include "json.hpp"
|
||||
#include "sign.hpp"
|
||||
#include <rublon/configuration.hpp>
|
||||
#include <rublon/core_handler_interface.hpp>
|
||||
#include <rublon/curl.hpp>
|
||||
#include <rublon/json.hpp>
|
||||
#include <rublon/sign.hpp>
|
||||
|
||||
#include <tl/expected.hpp>
|
||||
|
||||
#include <rublon/core_handler_interface.hpp>
|
||||
|
||||
namespace rublon {
|
||||
|
||||
template < typename HttpHandler = CURL >
|
||||
@ -32,13 +31,29 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
|
||||
log(LogLevel::Error, "Signature mismatch %s != %s ", xRubResp.c_str(), sign.data());
|
||||
return signatureMatch;
|
||||
}
|
||||
|
||||
bool containsException(const Document & coreResponse) const {
|
||||
|
||||
// if(resp.HasMember("status") and resp["status"].IsObject() and resp["result"].HasMember("exception")) {
|
||||
// const auto & exception = resp["result"]["exception"].GetString();
|
||||
// log(LogLevel::Error, "rublon core exception %s", exception);
|
||||
// return handleCoreException(exception);
|
||||
// }
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
return coreResponse.HasMember("status") and coreResponse["status"].GetString() == "ERROR"sv;
|
||||
}
|
||||
|
||||
bool isUnHealthy(const Document & coreResponse) const {
|
||||
return coreResponse.HasParseError() or not coreResponse.HasMember("result");
|
||||
}
|
||||
|
||||
protected:
|
||||
HttpHandler http{};
|
||||
|
||||
public:
|
||||
CoreHandler(const Configuration & config)
|
||||
: secretKey{config.parameters.secretKey}, url{config.parameters.apiServer}, bypass{config.parameters.bypass}, http{} {}
|
||||
: secretKey{config.secretKey.data()}, url{config.apiServer.data()}, bypass{config.bypass}, http{} {}
|
||||
|
||||
tl::expected< std::reference_wrapper< const Response >, Error > validateSignature(const Response & response) const {
|
||||
if(not responseSigned(response)) {
|
||||
@ -53,14 +68,16 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
|
||||
Document resp{&alloc};
|
||||
resp.Parse(response.body.c_str());
|
||||
|
||||
if(resp.HasParseError() or not resp.HasMember("result")) {
|
||||
log(LogLevel::Error, "rublon Core responded with broken data");
|
||||
log(LogLevel::Debug, "Begin, Core Response validation");
|
||||
|
||||
if(isUnHealthy(resp)) {
|
||||
log(LogLevel::Error, "Rublon Core responded with broken data");
|
||||
return tl::unexpected{CoreHandlerError{CoreHandlerError::BrokenData}};
|
||||
}
|
||||
|
||||
if(resp.HasMember("result") and resp["result"].IsObject() and resp["result"].HasMember("exception")) {
|
||||
|
||||
else if(containsException(resp)) {
|
||||
const auto & exception = resp["result"]["exception"].GetString();
|
||||
log(LogLevel::Error, "rublon core exception %s", exception);
|
||||
log(LogLevel::Error, "Rublon Core responded with '%s' exception", exception);
|
||||
return handleCoreException(exception);
|
||||
}
|
||||
|
||||
@ -69,28 +86,14 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
|
||||
|
||||
tl::unexpected< Error > handleCoreException(std::string_view exceptionString) const {
|
||||
if(exceptionString == "UserBypassedException" or exceptionString == "UserNotFoundException") {
|
||||
return tl::unexpected{Error{PamBaypass{}}};
|
||||
return tl::unexpected{Error{RublonAuthenticationInterrupt{RublonAuthenticationInterrupt::UserBaypass}}};
|
||||
} else {
|
||||
return tl::unexpected{
|
||||
Error{CoreHandlerError{CoreHandlerError::CoreException, std::string{exceptionString.data(), exceptionString.size()}}}};
|
||||
}
|
||||
}
|
||||
|
||||
tl::unexpected< Error > handleHttpError() const {
|
||||
if(bypass) {
|
||||
log(LogLevel::Warning, "User login bypass");
|
||||
return tl::unexpected{Error{PamBaypass{}}};
|
||||
} else {
|
||||
log(LogLevel::Warning, "User login deny due to HTTP error");
|
||||
return tl::unexpected{Error{PamDeny{}}};
|
||||
}
|
||||
}
|
||||
|
||||
tl::expected< Document, Error > handleError(const Error & error) const {
|
||||
if(error.is< HttpError >() and error.hasClass(HttpError::Error)) {
|
||||
return handleHttpError();
|
||||
}
|
||||
|
||||
return tl::unexpected{Error{error}};
|
||||
}
|
||||
|
||||
@ -105,20 +108,19 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
|
||||
}
|
||||
|
||||
tl::expected< Document, Error > request(RapidJSONPMRAlloc & mr, std::string_view path, const Document & body) const {
|
||||
memory::StrictMonotonic_4k_HeapResource memoryResource;
|
||||
memory::StrictMonotonic_8k_HeapResource memoryResource;
|
||||
|
||||
const auto validateSignature = [&](const auto & arg) { return this->validateSignature(arg); };
|
||||
const auto validateResponse = [&](const auto & arg) { return this->validateResponse(mr, arg); };
|
||||
const auto handleError = [&](const auto & error) { return this->handleError(error); };
|
||||
const auto pmrs = [&](auto txt) { return std::pmr::string{txt, &memoryResource}; };
|
||||
const auto pmrs = [&](const auto & txt) { return std::pmr::string{txt, &memoryResource}; };
|
||||
|
||||
Request request{&memoryResource};
|
||||
Response response{&memoryResource};
|
||||
|
||||
|
||||
request.headers["Content-Type"] = pmrs("application/json");
|
||||
request.headers["Accept"] = pmrs("application/json");
|
||||
stringifyTo(body, request.body);
|
||||
|
||||
signRequest(request);
|
||||
std::pmr::string uri{url + path.data(), &memoryResource};
|
||||
|
||||
|
||||
@ -15,7 +15,5 @@ class CoreHandlerInterface {
|
||||
rublon::log(LogLevel::Debug, "%s", "CoreHandlerInterface::request");
|
||||
return static_cast< const Impl * >(this)->request(mr, path, body);
|
||||
}
|
||||
|
||||
// tl::expected< Document, Error >
|
||||
};
|
||||
} // namespace rublon
|
||||
|
||||
@ -6,10 +6,8 @@
|
||||
#include <curl/curl.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <memory_resource>
|
||||
|
||||
@ -75,8 +73,8 @@ class CURL {
|
||||
|
||||
public:
|
||||
CURL() : curl{std::unique_ptr< ::CURL, void (*)(::CURL *) >(curl_easy_init(), curl_easy_cleanup)} {}
|
||||
|
||||
tl::expected< std::reference_wrapper< Response >, HttpError >
|
||||
|
||||
tl::expected< std::reference_wrapper< Response >, ConnectionError >
|
||||
request(std::string_view uri, const Request & request, Response & response) const {
|
||||
memory::MonotonicStackResource< 8 * 1024 > stackResource;
|
||||
|
||||
@ -105,7 +103,7 @@ class CURL {
|
||||
|
||||
if(res != CURLE_OK) {
|
||||
log(LogLevel::Error, "%s no response from Rublon server err:{%s}", "CURL", curl_easy_strerror(res));
|
||||
return tl::unexpected{HttpError{HttpError::Timeout, 0}};
|
||||
return tl::unexpected{ConnectionError{ConnectionError::Timeout, 0}};
|
||||
}
|
||||
|
||||
long http_code = 0;
|
||||
@ -113,7 +111,7 @@ class CURL {
|
||||
|
||||
if(http_code >= 500) {
|
||||
log(LogLevel::Error, "%s response with code %d ", "CURL", http_code);
|
||||
return tl::unexpected{HttpError{HttpError::Error, http_code}};
|
||||
return tl::unexpected{ConnectionError{ConnectionError::Error, http_code}};
|
||||
}
|
||||
|
||||
log(LogLevel::Debug, "Response:\n%s\n", response_data.c_str());
|
||||
|
||||
@ -1,21 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <rublon/non_owning_ptr.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <variant>
|
||||
|
||||
namespace rublon {
|
||||
class NoError {
|
||||
public:
|
||||
static constexpr int errorClass = 0;
|
||||
};
|
||||
|
||||
class HttpError {
|
||||
#define names constexpr static inline const char * errorClassPrettyName[]
|
||||
|
||||
class ConnectionError {
|
||||
public:
|
||||
enum ErrorClass { Timeout, Error };
|
||||
names = {"Timeout", "Error"};
|
||||
|
||||
constexpr HttpError() : errorClass{Timeout}, httpCode(200) {}
|
||||
constexpr HttpError(ErrorClass e, long httpCode) : errorClass{e}, httpCode(httpCode) {}
|
||||
constexpr static inline auto prettyName = "Connection Error";
|
||||
|
||||
constexpr ConnectionError() : errorClass{Timeout}, httpCode(200) {}
|
||||
constexpr ConnectionError(ErrorClass e, long httpCode) : errorClass{e}, httpCode(httpCode) {}
|
||||
|
||||
constexpr const char * what() const {
|
||||
return errorClassPrettyName[static_cast< int >(errorClass)];
|
||||
}
|
||||
|
||||
ErrorClass errorClass;
|
||||
long httpCode;
|
||||
@ -23,20 +29,34 @@ class HttpError {
|
||||
|
||||
class CoreHandlerError {
|
||||
public:
|
||||
enum ErrorClass { Unknown, BadSigature, CoreException, BrokenData };
|
||||
enum ErrorClass { BadSigature, CoreException, BrokenData };
|
||||
names = {"BadSigature", "CoreException", "BrokenData"};
|
||||
|
||||
CoreHandlerError(ErrorClass e = Unknown) : errorClass{e} {}
|
||||
constexpr static inline auto prettyName = "Core Handler Error";
|
||||
|
||||
CoreHandlerError(ErrorClass e = BadSigature) : errorClass{e} {}
|
||||
CoreHandlerError(ErrorClass e, std::string r) : errorClass{e}, reson{std::move(r)} {}
|
||||
|
||||
constexpr const char * what() const {
|
||||
return errorClassPrettyName[static_cast< int >(errorClass)];
|
||||
}
|
||||
|
||||
ErrorClass errorClass;
|
||||
std::string reson;
|
||||
};
|
||||
|
||||
class MethodError {
|
||||
public:
|
||||
enum ErrorClass { BadMethod };
|
||||
enum ErrorClass { BadMethod, BadUserInput, NoMethodAvailable };
|
||||
names = {"BadMethod", "BadUserInput", "NoMethodAvailable"};
|
||||
|
||||
constexpr MethodError(ErrorClass e) : errorClass{e} {}
|
||||
constexpr static inline auto prettyName = "Method Error";
|
||||
|
||||
constexpr MethodError(ErrorClass e = BadMethod) : errorClass{e} {}
|
||||
|
||||
constexpr const char * what() const {
|
||||
return errorClassPrettyName[static_cast< int >(errorClass)];
|
||||
}
|
||||
|
||||
ErrorClass errorClass;
|
||||
};
|
||||
@ -44,55 +64,50 @@ class MethodError {
|
||||
class WerificationError {
|
||||
public:
|
||||
enum ErrorClass { WrongCode };
|
||||
names = {"WrongCode"};
|
||||
|
||||
constexpr WerificationError(ErrorClass e) : errorClass{e} {}
|
||||
constexpr static inline auto prettyName = "Werification Error";
|
||||
|
||||
constexpr WerificationError(ErrorClass e = WrongCode) : errorClass{e} {}
|
||||
|
||||
constexpr const char * what() const {
|
||||
return errorClassPrettyName[static_cast< int >(errorClass)];
|
||||
}
|
||||
|
||||
ErrorClass errorClass;
|
||||
};
|
||||
|
||||
class Critical {
|
||||
class RublonAuthenticationInterrupt {
|
||||
public:
|
||||
enum ErrorClass { Nok };
|
||||
enum ErrorClass { UserBaypass, UserDenied, UserPending };
|
||||
names = {"UserBaypass", "UserDenied", "UserPending"};
|
||||
|
||||
Critical(ErrorClass e = Nok) : errorClass{e} {}
|
||||
constexpr static inline auto prettyName = "Rublon Authentication Interrupt";
|
||||
|
||||
ErrorClass errorClass;
|
||||
};
|
||||
RublonAuthenticationInterrupt(ErrorClass e = UserBaypass) : errorClass{e} {}
|
||||
|
||||
class PamBaypass {
|
||||
public:
|
||||
enum ErrorClass { Nok };
|
||||
|
||||
constexpr PamBaypass(ErrorClass e = Nok) : errorClass{e} {}
|
||||
|
||||
ErrorClass errorClass;
|
||||
};
|
||||
|
||||
class PamDeny {
|
||||
public:
|
||||
enum ErrorClass { Nok };
|
||||
|
||||
constexpr PamDeny(ErrorClass e = Nok) : errorClass{e} {}
|
||||
constexpr const char * what() const {
|
||||
return errorClassPrettyName[static_cast< int >(errorClass)];
|
||||
}
|
||||
|
||||
ErrorClass errorClass;
|
||||
};
|
||||
|
||||
class Error {
|
||||
using Error_t = std::variant< NoError, CoreHandlerError, HttpError, WerificationError, Critical, MethodError, PamBaypass, PamDeny >;
|
||||
using Error_t =
|
||||
std::variant< CoreHandlerError, ConnectionError, WerificationError, MethodError, RublonAuthenticationInterrupt >;
|
||||
Error_t _error;
|
||||
|
||||
public:
|
||||
enum Category { k_None, k_CoreHandlerError, k_SockerError, k_WerificationError, k_Critical, k_MethodError, k_PamBaypass, k_PamDeny };
|
||||
enum Category { k_CoreHandlerError, k_ConnectionError, k_WerificationError, k_MethodError, k_RublonAuthenticationInterrupt };
|
||||
|
||||
Error() = default;
|
||||
|
||||
Error(CoreHandlerError error) : _error{error} {}
|
||||
Error(HttpError error) : _error{error} {}
|
||||
Error(ConnectionError error) : _error{error} {}
|
||||
Error(MethodError error) : _error{error} {}
|
||||
Error(WerificationError error) : _error{error} {}
|
||||
Error(Critical error) : _error{error} {}
|
||||
Error(PamBaypass error) : _error{error} {}
|
||||
Error(PamDeny error) : _error{error} {}
|
||||
Error(RublonAuthenticationInterrupt error) : _error{error} {}
|
||||
|
||||
Error(const Error &) = default;
|
||||
Error(Error &&) = default;
|
||||
@ -100,6 +115,7 @@ class Error {
|
||||
Error & operator=(const Error &) = default;
|
||||
Error & operator=(Error &&) = default;
|
||||
|
||||
public:
|
||||
constexpr bool coreError() const {
|
||||
return _error.index() == 1;
|
||||
}
|
||||
@ -112,10 +128,18 @@ class Error {
|
||||
return static_cast< Category >(_error.index());
|
||||
}
|
||||
|
||||
constexpr const char * categoryName() const noexcept {
|
||||
return std::visit([](const auto & e) { return e.what(); }, _error);
|
||||
}
|
||||
|
||||
constexpr int errorClass() const noexcept {
|
||||
return std::visit([](const auto & e) { return static_cast< int >(e.errorClass); }, _error);
|
||||
}
|
||||
|
||||
constexpr const char * errorClassName() const noexcept {
|
||||
return std::visit([](const auto & e) { return e.prettyName; }, _error);
|
||||
}
|
||||
|
||||
template < typename E >
|
||||
constexpr bool is() const {
|
||||
return category() == Error{E{}}.category();
|
||||
@ -142,7 +166,7 @@ class Error {
|
||||
}
|
||||
};
|
||||
|
||||
constexpr bool operator==(const Error & e, const HttpError & socket) {
|
||||
constexpr bool operator==(const Error & e, const ConnectionError & socket) {
|
||||
return e.sockerError() && e.errorClass() == socket.errorClass;
|
||||
}
|
||||
|
||||
|
||||
@ -8,44 +8,41 @@
|
||||
#include <rublon/method/method_select.hpp>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace rublon {
|
||||
class Verify {};
|
||||
} // namespace rublon
|
||||
|
||||
namespace rublon {
|
||||
std::pmr::string osName(std::pmr::memory_resource *mr) {
|
||||
std::pmr::string osName(std::pmr::memory_resource * mr) {
|
||||
memory::MonotonicStackResource< 8 * 1024 > stackResource;
|
||||
using Params = Configuration::Parameters;
|
||||
Params configValues;
|
||||
|
||||
/// TODO move this to utils
|
||||
std::ifstream file(std::filesystem::path{"/etc/os-release"});
|
||||
if(not file.good())
|
||||
return {"unknown", mr};
|
||||
|
||||
|
||||
std::pmr::string line{&stackResource};
|
||||
line.reserve(100);
|
||||
|
||||
|
||||
/// TODO introduce toKeyValue function in utils
|
||||
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);
|
||||
|
||||
if(_key == "PRETTY_NAME"){
|
||||
_key = line.substr(0, posEqual);
|
||||
_value = line.substr(posEqual + 1);
|
||||
|
||||
if(_key == "PRETTY_NAME") {
|
||||
return {_value, mr};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {"unknown", mr};
|
||||
}
|
||||
|
||||
@ -55,12 +52,6 @@ class Init : public AuthenticationStep< Init< MethodSelect_t > > {
|
||||
|
||||
const char * apiPath = "/api/transaction/init";
|
||||
|
||||
tl::expected< MethodSelect_t, Error > createMethod(const Document & coreResponse) const {
|
||||
const auto & rublonResponse = coreResponse["result"];
|
||||
std::string tid = rublonResponse["tid"].GetString();
|
||||
return MethodSelect_t{this->_systemToken, tid, rublonResponse["methods"]};
|
||||
}
|
||||
|
||||
template < typename PamInfo_t >
|
||||
void addPamInfo(Document & coreRequest, const PamInfo_t & pam) const {
|
||||
auto & alloc = coreRequest.GetAllocator();
|
||||
@ -73,32 +64,65 @@ class Init : public AuthenticationStep< Init< MethodSelect_t > > {
|
||||
std::pmr::string releaseInfo{&stackResource};
|
||||
auto & alloc = coreRequest.GetAllocator();
|
||||
|
||||
const auto os= osName(&stackResource);
|
||||
|
||||
if(os== "unknown") {
|
||||
const auto os = osName(&stackResource);
|
||||
|
||||
if(os == "unknown") {
|
||||
log(LogLevel::Warning, "No OS information available");
|
||||
}
|
||||
|
||||
Value osNamePretty{os.data(), static_cast< unsigned >(os.size()), alloc};
|
||||
Value ip{pam.ip().get(), alloc};
|
||||
|
||||
|
||||
Value params{rapidjson::kObjectType};
|
||||
params.AddMember("userIP", ip, alloc);
|
||||
params.AddMember("appVer", "v.0.0.1", alloc); /// TODO add version to cmake
|
||||
params.AddMember("os", osNamePretty, alloc);
|
||||
|
||||
|
||||
coreRequest.AddMember("params", std::move(params), alloc);
|
||||
}
|
||||
|
||||
template < typename PamInfo_t >
|
||||
tl::expected< std::reference_wrapper< const Document >, Error > checkEnrolement(const Document & coreResponse,
|
||||
const PamInfo_t & pam) const {
|
||||
using namespace std::string_view_literals;
|
||||
const auto & resp = coreResponse;
|
||||
|
||||
if(resp.HasMember("result") and resp["result"].IsObject() and resp["result"].HasMember("status")) {
|
||||
const auto & status = resp["result"]["status"].GetString();
|
||||
log(LogLevel::Warning, "Got enrolement message with stats %s", status);
|
||||
if(status == "pending"sv and resp["result"].HasMember("webURI")) {
|
||||
const auto & weburi = resp["result"]["webURI"].GetString();
|
||||
pam.print("It seams that your account is not configured properly,\nplease contact your administrator for more information");
|
||||
pam.print("also, please visit %s", weburi);
|
||||
|
||||
return tl::unexpected{Error{RublonAuthenticationInterrupt{RublonAuthenticationInterrupt::UserPending}}};
|
||||
}
|
||||
if(status == "denied"sv) {
|
||||
pam.print("It seams that your account is disabled, please contact your administrator for more information");
|
||||
return tl::unexpected{Error{RublonAuthenticationInterrupt{RublonAuthenticationInterrupt::UserDenied}}};
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO server response is malformed
|
||||
|
||||
return coreResponse;
|
||||
}
|
||||
|
||||
tl::expected< MethodSelect_t, Error > createMethod(const Document & coreResponse) const {
|
||||
const auto & rublonResponse = coreResponse["result"];
|
||||
std::string tid = rublonResponse["tid"].GetString();
|
||||
return MethodSelect_t{this->_systemToken, tid, rublonResponse["methods"]};
|
||||
}
|
||||
|
||||
public:
|
||||
const char * name = "Initialization";
|
||||
|
||||
Init(const rublon::Configuration & config) : base_t(config.parameters.systemToken, "") {}
|
||||
Init(const rublon::Configuration & config) : base_t(config.systemToken.data(), "") {}
|
||||
|
||||
/// TODO add core handler interface
|
||||
template < typename Hander_t, typename PamInfo_t = LinuxPam >
|
||||
tl::expected< MethodSelect_t, Error > handle(const CoreHandlerInterface< Hander_t > & coreHandler, const PamInfo_t & pam) const {
|
||||
const auto createMethod = [&](const auto & coreResponse) { return this->createMethod(coreResponse); };
|
||||
const auto createMethod = [&](const auto & coreResponse) { return this->createMethod(coreResponse); };
|
||||
const auto checkEnrolement = [&](const auto & coreResponse) { return this->checkEnrolement(coreResponse, pam); };
|
||||
|
||||
RapidJSONPMRStackAlloc< 2048 > alloc{};
|
||||
Document body{rapidjson::kObjectType, &alloc};
|
||||
@ -109,6 +133,7 @@ class Init : public AuthenticationStep< Init< MethodSelect_t > > {
|
||||
|
||||
return coreHandler
|
||||
.request(alloc, apiPath, body) //
|
||||
.and_then(checkEnrolement)
|
||||
.and_then(createMethod);
|
||||
}
|
||||
};
|
||||
|
||||
85
PAM/ssh/include/rublon/memory.hpp
Normal file
85
PAM/ssh/include/rublon/memory.hpp
Normal file
@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory_resource>
|
||||
|
||||
namespace rublon {
|
||||
namespace memory {
|
||||
struct holder {
|
||||
static inline std::pmr::memory_resource * _mr = std::pmr::get_default_resource();
|
||||
};
|
||||
|
||||
inline void set_default_resource(std::pmr::memory_resource * memory_resource) {
|
||||
holder{}._mr = memory_resource;
|
||||
}
|
||||
|
||||
inline std::pmr::memory_resource * default_resource() {
|
||||
return holder{}._mr;
|
||||
}
|
||||
|
||||
template < std::size_t N >
|
||||
class MonotonicStackResource : public std::pmr::monotonic_buffer_resource {
|
||||
char _buffer[N];
|
||||
|
||||
public:
|
||||
MonotonicStackResource() : std::pmr::monotonic_buffer_resource{_buffer, N, std::pmr::null_memory_resource()} {}
|
||||
};
|
||||
|
||||
template < std::size_t N >
|
||||
class UnsynchronizedStackResource : public std::pmr::unsynchronized_pool_resource {
|
||||
MonotonicStackResource< N > _upstream;
|
||||
|
||||
public:
|
||||
UnsynchronizedStackResource() : std::pmr::unsynchronized_pool_resource{&_upstream} {}
|
||||
};
|
||||
|
||||
class MonotonicHeapResourceBase {
|
||||
public:
|
||||
std::pmr::memory_resource * _upstream{};
|
||||
std::size_t _size{};
|
||||
void * _buffer{nullptr};
|
||||
|
||||
MonotonicHeapResourceBase(std::size_t size) : _upstream{default_resource()}, _size{size}, _buffer{_upstream->allocate(size)} {}
|
||||
|
||||
~MonotonicHeapResourceBase() {
|
||||
if(_buffer)
|
||||
_upstream->deallocate(_buffer, _size);
|
||||
}
|
||||
};
|
||||
|
||||
template < std::size_t N >
|
||||
class MonotonicHeapResource : MonotonicHeapResourceBase, public std::pmr::monotonic_buffer_resource {
|
||||
public:
|
||||
MonotonicHeapResource()
|
||||
: MonotonicHeapResourceBase{N}, std::pmr::monotonic_buffer_resource{this->_buffer, this->_size, default_resource()} {}
|
||||
};
|
||||
|
||||
template < std::size_t N >
|
||||
class StrictMonotonicHeapResource : MonotonicHeapResourceBase, public std::pmr::monotonic_buffer_resource {
|
||||
public:
|
||||
StrictMonotonicHeapResource()
|
||||
: MonotonicHeapResourceBase{N},
|
||||
std::pmr::monotonic_buffer_resource{this->_buffer, this->_size, std::pmr::null_memory_resource()} {}
|
||||
};
|
||||
|
||||
using StrictMonotonic_1k_HeapResource = StrictMonotonicHeapResource< 1 * 1024 >;
|
||||
using StrictMonotonic_2k_HeapResource = StrictMonotonicHeapResource< 2 * 1024 >;
|
||||
using StrictMonotonic_4k_HeapResource = StrictMonotonicHeapResource< 4 * 1024 >;
|
||||
using StrictMonotonic_8k_HeapResource = StrictMonotonicHeapResource< 8 * 1024 >;
|
||||
|
||||
using Monotonic_1k_HeapResource = MonotonicHeapResource< 1 * 1024 >;
|
||||
using Monotonic_2k_HeapResource = MonotonicHeapResource< 2 * 1024 >;
|
||||
using Monotonic_4k_HeapResource = MonotonicHeapResource< 4 * 1024 >;
|
||||
using Monotonic_8k_HeapResource = MonotonicHeapResource< 8 * 1024 >;
|
||||
} // namespace memory
|
||||
|
||||
class RublonMemory {
|
||||
public:
|
||||
std::byte sharedMemory[32 * 1024] = {};
|
||||
std::pmr::monotonic_buffer_resource mr{sharedMemory, std::size(sharedMemory)};
|
||||
std::pmr::unsynchronized_pool_resource rublonPoolResource{&mr};
|
||||
|
||||
RublonMemory() {
|
||||
std::pmr::set_default_resource(&rublonPoolResource);
|
||||
}
|
||||
};
|
||||
} // namespace rublon
|
||||
@ -10,8 +10,8 @@
|
||||
#include <rublon/pam_action.hpp>
|
||||
|
||||
#include <rublon/method/OTP.hpp>
|
||||
#include <rublon/method/SMS.hpp>
|
||||
#include <rublon/method/PUSH.hpp>
|
||||
#include <rublon/method/SMS.hpp>
|
||||
|
||||
template < class F >
|
||||
struct return_type;
|
||||
@ -73,7 +73,7 @@ class PostMethod : public rublon::AuthenticationStep< PostMethod > {
|
||||
return MethodProxy{method::OTP{this->_systemToken, std::move(tid)}};
|
||||
} else if(_method == "sms") {
|
||||
return MethodProxy{method::SMS{this->_systemToken, std::move(tid)}};
|
||||
} else if(_method == "push"){
|
||||
} else if(_method == "push") {
|
||||
return MethodProxy{method::PUSH{this->_systemToken, std::move(tid)}};
|
||||
}
|
||||
|
||||
@ -136,15 +136,15 @@ class MethodSelect {
|
||||
memory::StrictMonotonic_2k_HeapResource memoryResource;
|
||||
std::pmr::map< int, std::string > methods_id{&memoryResource};
|
||||
pam.print("select method: ");
|
||||
|
||||
auto logMethodAvailable = [](auto &method){
|
||||
|
||||
auto logMethodAvailable = [](auto & method) { //
|
||||
rublon::log(LogLevel::Debug, "Method %s found", method.c_str());
|
||||
};
|
||||
auto logMethodNotAvailable = [](auto &method){
|
||||
auto logMethodNotAvailable = [](auto & method) {
|
||||
rublon::log(LogLevel::Debug, "Method %s found, but has no handler", method.c_str());
|
||||
};
|
||||
|
||||
auto print_methods = [&]() {
|
||||
|
||||
auto printAvailableMethods = [&]() -> tl::expected< int, MethodError > {
|
||||
int i{};
|
||||
for(const auto & method : _methods) {
|
||||
if(method == "totp") {
|
||||
@ -160,57 +160,75 @@ class MethodSelect {
|
||||
methods_id[++i] = method;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(method == "push" ){
|
||||
|
||||
if(method == "push") {
|
||||
logMethodAvailable(method);
|
||||
pam.print("%d: Mobile PUSH", i + 1);
|
||||
methods_id[++i] = method;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
logMethodNotAvailable(method);
|
||||
}
|
||||
if(i == 0) {
|
||||
return tl::unexpected(MethodError(MethodError::ErrorClass::NoMethodAvailable));
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
const auto toMethodError = [&](conv::Error /*methodid*/) -> Error {
|
||||
const auto toMethodError = [&](conv::Error /*methodid*/) -> MethodError {
|
||||
pam.print("Input is not an number, please correct");
|
||||
return MethodError{MethodError::BadMethod};
|
||||
return MethodError{MethodError::BadUserInput};
|
||||
};
|
||||
|
||||
const auto createMethod = [&](std::uint32_t methodid) -> tl::expected< PostMethod, Error > {
|
||||
const auto createMethod = [&](std::uint32_t methodid) -> tl::expected< PostMethod, MethodError > {
|
||||
auto hasMethod = methods_id.find(methodid) != methods_id.end();
|
||||
pam.print("\t selected: %s", hasMethod ? methods_id.at(methodid).c_str() : "unknown option");
|
||||
if(!hasMethod) {
|
||||
return tl::unexpected{Error{Critical{}}}; /// TODO change to some more meaningfull error
|
||||
return tl::unexpected{MethodError(MethodError::BadMethod)};
|
||||
} else {
|
||||
log(LogLevel::Info, "User selected option %d{%s}", methodid, methods_id.at(methodid).c_str());
|
||||
return PostMethod{_systemToken, _tid, methods_id.at(methodid)};
|
||||
}
|
||||
};
|
||||
|
||||
const auto askForMethodAgain = [&](const Error &) {
|
||||
print_methods();
|
||||
const auto askForMethodAgain = [&]() -> tl::expected< PostMethod, MethodError > {
|
||||
printAvailableMethods();
|
||||
return pam
|
||||
.scan(conv::to_uint32, "\nSelect method [1-%d]: ", methods_id.size()) //
|
||||
.transform_error(toMethodError)
|
||||
.and_then(createMethod);
|
||||
/// TODO or_else(printErrorAndDenyAccess); ??
|
||||
};
|
||||
|
||||
const auto askForMethod = [&]() -> tl::expected<uint32_t,conv::Error>{
|
||||
if(methods_id.size() == 1){
|
||||
|
||||
const auto askForMethod = [&](int methods_number) -> tl::expected< uint32_t, MethodError > {
|
||||
if(methods_number == 1) {
|
||||
pam.print("Only one method available");
|
||||
return 1;
|
||||
}
|
||||
return pam.scan(conv::to_uint32, "\nSelect method [1-%d]: ", methods_id.size());
|
||||
return pam.scan(conv::to_uint32, "\nSelect method [1-%d]: ", methods_number).transform_error(toMethodError);
|
||||
};
|
||||
|
||||
print_methods();
|
||||
|
||||
return askForMethod() //
|
||||
.transform_error(toMethodError)
|
||||
|
||||
const auto handleErrors = [&](const MethodError & e) -> tl::expected< PostMethod, MethodError > {
|
||||
switch(e.errorClass) {
|
||||
case MethodError::BadMethod: // User provided a number but the number if not found
|
||||
return askForMethodAgain();
|
||||
case MethodError::BadUserInput: // User provided id is invalid (NAN)
|
||||
return askForMethodAgain();
|
||||
case MethodError::NoMethodAvailable:
|
||||
/// TODO print enrolement
|
||||
default:
|
||||
return tl::unexpected(e);
|
||||
}
|
||||
};
|
||||
|
||||
/// TODO make this some kind of global
|
||||
const auto toGenericError = [](const MethodError & e) -> Error { return Error{e}; };
|
||||
|
||||
return printAvailableMethods() //
|
||||
.and_then(askForMethod)
|
||||
.and_then(createMethod)
|
||||
.or_else(askForMethodAgain);
|
||||
.or_else(handleErrors)
|
||||
.map_error(toGenericError);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -30,5 +30,4 @@ class NonOwningPtr {
|
||||
return get();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rublon
|
||||
|
||||
@ -20,6 +20,10 @@ class AuthenticationStatus {
|
||||
return false;
|
||||
}
|
||||
|
||||
Action action() const {
|
||||
return _action;
|
||||
}
|
||||
|
||||
private:
|
||||
Action _action;
|
||||
};
|
||||
|
||||
@ -16,26 +16,29 @@ namespace rublon {
|
||||
// pam.print("Rublon configuration does not exists or is invalid");
|
||||
// pam.print("\tcheck '%s' for more details\n", details::logPath());
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
|
||||
template < typename Pam_T, typename CoreHandler_T>
|
||||
class RublonBase {
|
||||
Pam_T & pam;
|
||||
Configuration config;
|
||||
|
||||
|
||||
class RublonSession {
|
||||
public:
|
||||
RublonBase(Pam_T & pam, Configuration config) : pam{pam}, config{config} {}
|
||||
|
||||
|
||||
|
||||
// int authenticate() {
|
||||
// std::byte sharedMemory[32 * 1024] = {};
|
||||
// std::pmr::monotonic_buffer_resource mr{sharedMemory, std::size(sharedMemory)};
|
||||
// std::pmr::unsynchronized_pool_resource rublonPoolResource{&mr};
|
||||
// std::pmr::set_default_resource(&rublonPoolResource);
|
||||
// }
|
||||
};
|
||||
|
||||
template < typename Pam_T, typename CoreHandler_T >
|
||||
class RublonBase {
|
||||
Pam_T & pam;
|
||||
Configuration config{};
|
||||
|
||||
void initializeLogs() {
|
||||
details::initLog();
|
||||
}
|
||||
|
||||
public:
|
||||
RublonBase(Pam_T & pam) : pam{pam} {}
|
||||
|
||||
// AuthenticationStatus authenticate() {}
|
||||
};
|
||||
|
||||
using RublonLinux = RublonBase< LinuxPam, CoreHandler< CURL > >;
|
||||
|
||||
} // namespace rublon
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <string_view>
|
||||
|
||||
namespace rublon {
|
||||
|
||||
|
||||
@ -19,10 +19,13 @@
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/pam_modules.h>
|
||||
#include <sys/stat.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
#include <rublon/memory.hpp>
|
||||
|
||||
namespace rublon {
|
||||
|
||||
inline bool fileGood(const std::filesystem::path & path) {
|
||||
@ -44,74 +47,7 @@ inline bool readFile(const std::filesystem::path & path, T & destination) {
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace memory {
|
||||
struct holder {
|
||||
static inline std::pmr::memory_resource * _mr = std::pmr::get_default_resource();
|
||||
};
|
||||
|
||||
inline void set_default_resource(std::pmr::memory_resource * memory_resource) {
|
||||
holder{}._mr = memory_resource;
|
||||
}
|
||||
|
||||
inline std::pmr::memory_resource * default_resource() {
|
||||
return holder{}._mr;
|
||||
}
|
||||
|
||||
template < std::size_t N >
|
||||
class MonotonicStackResource : public std::pmr::monotonic_buffer_resource {
|
||||
char _buffer[N];
|
||||
|
||||
public:
|
||||
MonotonicStackResource() : std::pmr::monotonic_buffer_resource{_buffer, N, std::pmr::null_memory_resource()} {}
|
||||
};
|
||||
|
||||
template < std::size_t N >
|
||||
class UnsynchronizedStackResource : public std::pmr::unsynchronized_pool_resource {
|
||||
MonotonicStackResource< N > _upstream;
|
||||
|
||||
public:
|
||||
UnsynchronizedStackResource() : std::pmr::unsynchronized_pool_resource{&_upstream} {}
|
||||
};
|
||||
|
||||
class MonotonicHeapResourceBase {
|
||||
public:
|
||||
std::pmr::memory_resource * _upstream{};
|
||||
std::size_t _size{};
|
||||
void * _buffer{nullptr};
|
||||
|
||||
MonotonicHeapResourceBase(std::size_t size) : _upstream{default_resource()}, _size{size}, _buffer{_upstream->allocate(size)} {}
|
||||
|
||||
~MonotonicHeapResourceBase() {
|
||||
if(_buffer)
|
||||
_upstream->deallocate(_buffer, _size);
|
||||
}
|
||||
};
|
||||
|
||||
template < std::size_t N >
|
||||
class MonotonicHeapResource : MonotonicHeapResourceBase, public std::pmr::monotonic_buffer_resource {
|
||||
public:
|
||||
MonotonicHeapResource()
|
||||
: MonotonicHeapResourceBase{N}, std::pmr::monotonic_buffer_resource{this->_buffer, this->_size, default_resource()} {}
|
||||
};
|
||||
|
||||
template < std::size_t N >
|
||||
class StrictMonotonicHeapResource : MonotonicHeapResourceBase, public std::pmr::monotonic_buffer_resource {
|
||||
public:
|
||||
StrictMonotonicHeapResource()
|
||||
: MonotonicHeapResourceBase{N},
|
||||
std::pmr::monotonic_buffer_resource{this->_buffer, this->_size, std::pmr::null_memory_resource()} {}
|
||||
};
|
||||
|
||||
using StrictMonotonic_1k_HeapResource = StrictMonotonicHeapResource< 1 * 1024 >;
|
||||
using StrictMonotonic_2k_HeapResource = StrictMonotonicHeapResource< 2 * 1024 >;
|
||||
using StrictMonotonic_4k_HeapResource = StrictMonotonicHeapResource< 4 * 1024 >;
|
||||
using StrictMonotonic_8k_HeapResource = StrictMonotonicHeapResource< 8 * 1024 >;
|
||||
|
||||
using Monotonic_1k_HeapResource = MonotonicHeapResource< 1 * 1024 >;
|
||||
using Monotonic_2k_HeapResource = MonotonicHeapResource< 2 * 1024 >;
|
||||
using Monotonic_4k_HeapResource = MonotonicHeapResource< 4 * 1024 >;
|
||||
using Monotonic_8k_HeapResource = MonotonicHeapResource< 8 * 1024 >;
|
||||
} // namespace memory
|
||||
|
||||
enum class LogLevel { Debug, Info, Warning, Error };
|
||||
|
||||
@ -129,14 +65,28 @@ constexpr bool syncLogFile = true;
|
||||
|
||||
namespace details {
|
||||
constexpr const char * logPath() {
|
||||
constexpr auto path = "/tmp/rublon-ssh.log";
|
||||
constexpr auto path = "/var/log/rublon-ssh.log";
|
||||
return path;
|
||||
}
|
||||
|
||||
static void doLog(LogLevel level, const char * line) noexcept {
|
||||
auto fp = std::unique_ptr< FILE, int (*)(FILE *) >(fopen(logPath(), "a"), fclose);
|
||||
inline bool logExists() noexcept {
|
||||
return std::unique_ptr< FILE, int (*)(FILE *) >(fopen(logPath(), "r"), fclose).get();
|
||||
}
|
||||
|
||||
inline void initLog() noexcept {
|
||||
if(not logExists()) {
|
||||
auto fp = fopen(logPath(), "w");
|
||||
if(fp) {
|
||||
fclose(fp);
|
||||
chmod(logPath(), S_IRUSR | S_IWUSR | S_IRGRP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void doLog(LogLevel level, const char * line) noexcept {
|
||||
auto fp = std::unique_ptr< FILE, int (*)(FILE *) >(fopen(logPath(), "a+"), fclose);
|
||||
if(fp) {
|
||||
///TODO add transaction ID
|
||||
/// TODO add transaction ID
|
||||
fprintf(fp.get(), "%s [%s] %s\n", dateStr().data(), LogLevelNames[( int ) level], line);
|
||||
if(syncLogFile)
|
||||
sync();
|
||||
@ -160,16 +110,15 @@ void log(LogLevel level, const char * fmt, Ti &&... ti) noexcept {
|
||||
details::doLog(level, line.data());
|
||||
}
|
||||
|
||||
|
||||
class PrintUser{
|
||||
class PrintUser {
|
||||
public:
|
||||
template < typename Printer >
|
||||
PrintUser(Printer & p) : _impl{std::make_unique< ModelImpl >(p)} {}
|
||||
|
||||
private:
|
||||
struct model{
|
||||
struct model {
|
||||
virtual ~model();
|
||||
virtual void print(std::string_view line) const=0;
|
||||
virtual void print(std::string_view line) const = 0;
|
||||
};
|
||||
|
||||
template < typename Printer_T >
|
||||
@ -181,20 +130,19 @@ class PrintUser{
|
||||
Printer_T & _printer;
|
||||
};
|
||||
|
||||
std::unique_ptr<model> _impl;
|
||||
std::unique_ptr< model > _impl;
|
||||
};
|
||||
|
||||
namespace conv{
|
||||
namespace conv {
|
||||
inline bool to_bool(std::string_view value) {
|
||||
auto * buf = ( char * ) alloca(value.size() + 1);
|
||||
buf[value.size()] = '\0';
|
||||
auto asciitolower = [](char in) { return in - ((in <= 'Z' && in >= 'A') ? ('Z' - 'z') : 0); };
|
||||
|
||||
|
||||
std::transform(value.cbegin(), value.cend(), buf, asciitolower);
|
||||
return strcmp(buf, "true") == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
enum class Error { OutOfRange, NotANumber };
|
||||
inline tl::expected< std::uint32_t, Error > to_uint32(std::string_view userinput) noexcept {
|
||||
try {
|
||||
@ -205,7 +153,7 @@ namespace conv{
|
||||
return tl::make_unexpected(Error::OutOfRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace conv
|
||||
|
||||
namespace details {
|
||||
static inline std::string_view ltrim(std::string_view s) {
|
||||
|
||||
@ -217,7 +217,7 @@ class WebSocket {
|
||||
log(LogLevel::Debug, "start listening");
|
||||
ws.connect("wss://staging-core.rublon.net/ws/socket.io/");
|
||||
|
||||
const auto attachToTransactionConfirmationChannel = [&](const auto & status) -> tl::expected< bool, bool > {
|
||||
const auto attachToTransactionConfirmationChannel = [&](const auto & /*status*/) -> tl::expected< bool, bool > {
|
||||
/// TODO check status
|
||||
auto message = std::dynamic_pointer_cast< sio::object_message >(sio::object_message::create());
|
||||
message->insert("channel", "transactionConfirmation." + _token);
|
||||
@ -226,7 +226,7 @@ class WebSocket {
|
||||
return true; /// TODO
|
||||
};
|
||||
|
||||
const auto waitForUserAction = [&](const auto & connected) -> tl::expected< WebSocketSingleShotEventListener::Status, bool > {
|
||||
const auto waitForUserAction = [&](const auto & /*connected*/) -> tl::expected< WebSocketSingleShotEventListener::Status, bool > {
|
||||
WebSocketSingleShotEventListener eventListener{ws, _lock, _cond};
|
||||
return eventListener.waitForEvent();
|
||||
};
|
||||
|
||||
@ -5,11 +5,7 @@
|
||||
#include <security/pam_modules.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <rublon/init.hpp>
|
||||
#include <rublon/json.hpp>
|
||||
#include <rublon/pam.hpp>
|
||||
#include <rublon/rublon.hpp>
|
||||
#include <rublon/utils.hpp>
|
||||
|
||||
#define DLL_PUBLIC __attribute__((visibility("default")))
|
||||
|
||||
@ -30,13 +26,11 @@ DLL_PUBLIC int pam_sm_acct_mgmt([[maybe_unused]] pam_handle_t * pamh,
|
||||
DLL_PUBLIC int
|
||||
pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) {
|
||||
using namespace rublon;
|
||||
|
||||
rublon::log(LogLevel::Debug, "flags: {%d}, argc: {%d}", flags, argc);
|
||||
// std::freopen(rublon::details::logPath(), "a+", stdout);
|
||||
// std::freopen(rublon::details::logPath(), "a+", stderr);
|
||||
|
||||
|
||||
LinuxPam pam{pamh};
|
||||
|
||||
|
||||
RublonLinux rublon{pam};
|
||||
|
||||
auto rublonConfig = ConfigurationFactory{}.systemConfig();
|
||||
if(not rublonConfig.has_value()) {
|
||||
pam.print("\n");
|
||||
@ -44,11 +38,8 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu
|
||||
pam.print("\tcheck '%s' for more details\n", details::logPath());
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
std::byte sharedMemory[32 * 1024] = {};
|
||||
std::pmr::monotonic_buffer_resource mr{sharedMemory, std::size(sharedMemory)};
|
||||
std::pmr::unsynchronized_pool_resource rublonPoolResource{&mr};
|
||||
std::pmr::set_default_resource(&rublonPoolResource);
|
||||
|
||||
RublonMemory mem;
|
||||
|
||||
CoreHandler CH{rublonConfig.value()};
|
||||
|
||||
@ -56,44 +47,69 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu
|
||||
auto confirmMethod = [&](const PostMethod & confirm) { return confirm.fire(CH); };
|
||||
auto confirmCode = [&](const MethodProxy & method) { return method.fire(CH, pam); };
|
||||
|
||||
auto allowLogin = [&](const AuthenticationStatus & status) -> tl::expected< int, Error > {
|
||||
if(status.userAuthorized()) {
|
||||
rublon::log(rublon::LogLevel::Info, "Auth OK");
|
||||
pam.print("RUBLON authentication SUCCESS!\n");
|
||||
return PAM_SUCCESS;
|
||||
} else {
|
||||
rublon::log(rublon::LogLevel::Info, "User unauthorized");
|
||||
pam.print("RUBLON authentication FAILED");
|
||||
return PAM_MAXTRIES;
|
||||
auto printAuthMessageAndExit = [&](const AuthenticationStatus status) {
|
||||
switch(status.action()) {
|
||||
case AuthenticationStatus::Action::Bypass:
|
||||
pam.print("\n\tRUBLON authentication bypased");
|
||||
return PAM_SUCCESS;
|
||||
|
||||
case AuthenticationStatus::Action::Denied:
|
||||
pam.print("\n\tRUBLON authentication FAILED");
|
||||
return PAM_MAXTRIES;
|
||||
|
||||
case AuthenticationStatus::Action::Confirmed:
|
||||
pam.print("\n\tRUBLON authentication SUCCESS");
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
pam.print("RUBLON connector has exited with unknown code, access DENY!\n");
|
||||
return PAM_MAXTRIES;
|
||||
};
|
||||
|
||||
auto allowLogin = [&](const AuthenticationStatus & status) -> tl::expected< int, Error > {
|
||||
return printAuthMessageAndExit(status);
|
||||
};
|
||||
|
||||
auto mapError = [&](const Error & error) -> tl::expected< int, Error > {
|
||||
rublon::log(
|
||||
LogLevel::Error, "auth problems due to %d class and %d category", error.errorClass(), static_cast< int >(error.category()));
|
||||
if(error.is< PamBaypass >()) {
|
||||
pam.print("\n RUBLON authentication bypased");
|
||||
return PAM_SUCCESS;
|
||||
log(LogLevel::Error, "Authorization interrupted with {%s::%s}", error.errorClassName(), error.categoryName());
|
||||
if(error.is< RublonAuthenticationInterrupt >()) {
|
||||
switch(error.get< RublonAuthenticationInterrupt >().errorClass) {
|
||||
case RublonAuthenticationInterrupt::ErrorClass::UserBaypass:
|
||||
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
|
||||
case RublonAuthenticationInterrupt::ErrorClass::UserDenied:
|
||||
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
||||
case RublonAuthenticationInterrupt::ErrorClass::UserPending:
|
||||
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
|
||||
}
|
||||
}
|
||||
|
||||
if(error.is< HttpError >()) {
|
||||
pam.print("\n RUBLON server unavalible");
|
||||
if(rublonConfig->parameters.bypass) {
|
||||
pam.print("\n RUBLON authentication bypased");
|
||||
return PAM_SUCCESS;
|
||||
if(error.is< MethodError >()) {
|
||||
switch(error.get< MethodError >().errorClass) {
|
||||
case MethodError::ErrorClass::BadMethod:
|
||||
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
||||
case MethodError::ErrorClass::BadUserInput:
|
||||
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
||||
case MethodError::ErrorClass::NoMethodAvailable:
|
||||
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(error.is< ConnectionError >()) {
|
||||
if(rublonConfig->bypass) {
|
||||
pam.print("Connection Error, bypass enabled");
|
||||
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
|
||||
} else {
|
||||
pam.print("RUBLON authentication FAILED");
|
||||
return PAM_MAXTRIES;
|
||||
pam.print("Connection Error, bypass disabled");
|
||||
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
||||
}
|
||||
}
|
||||
|
||||
if(error.is< CoreHandlerError >()) {
|
||||
pam.print("\n RUBLON server returned '%s' exception", error.get< CoreHandlerError >().reson.c_str());
|
||||
}
|
||||
pam.print("RUBLON authentication FAILED");
|
||||
rublon::log(LogLevel::Warning, "User login failed");
|
||||
|
||||
return PAM_MAXTRIES;
|
||||
|
||||
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
||||
};
|
||||
|
||||
auto ret = Init{rublonConfig.value()}
|
||||
|
||||
@ -9,97 +9,81 @@
|
||||
#include <rublon/core_handler.hpp>
|
||||
|
||||
using namespace rublon;
|
||||
class CoreHandlerTestable : public CoreHandler<HttpHandlerMock> {
|
||||
public:
|
||||
CoreHandlerTestable(rublon::Configuration _conf = conf)
|
||||
: CoreHandler<HttpHandlerMock>{_conf} {}
|
||||
auto &_http() { return http; }
|
||||
class CoreHandlerTestable : public CoreHandler< HttpHandlerMock > {
|
||||
public:
|
||||
CoreHandlerTestable(rublon::Configuration _conf = conf) : CoreHandler< HttpHandlerMock >{_conf} {}
|
||||
auto & _http() {
|
||||
return http;
|
||||
}
|
||||
|
||||
tl::expected<Document, Error> request(std::string_view path,
|
||||
const Document &body) {
|
||||
static auto mr = std::pmr::get_default_resource();
|
||||
static RapidJSONPMRAlloc alloc{mr};
|
||||
return CoreHandler<HttpHandlerMock>::request(alloc, path, body);
|
||||
}
|
||||
tl::expected< Document, Error > request(std::string_view path, const Document & body) {
|
||||
static auto mr = std::pmr::get_default_resource();
|
||||
static RapidJSONPMRAlloc alloc{mr};
|
||||
return CoreHandler< HttpHandlerMock >::request(alloc, path, body);
|
||||
}
|
||||
};
|
||||
|
||||
class CoreHandlerTests : public testing::Test {
|
||||
public:
|
||||
CoreHandlerTests() : http{sut._http()} { doc.SetObject(); }
|
||||
public:
|
||||
CoreHandlerTests() : http{sut._http()} {
|
||||
doc.SetObject();
|
||||
}
|
||||
|
||||
CoreHandlerTestable sut;
|
||||
HttpHandlerMock &http;
|
||||
rublon::Response _res{std::pmr::get_default_resource()};
|
||||
CoreHandlerTestable sut;
|
||||
HttpHandlerMock & http;
|
||||
rublon::Response _res{std::pmr::get_default_resource()};
|
||||
|
||||
Document doc;
|
||||
Document doc;
|
||||
};
|
||||
|
||||
using namespace testing;
|
||||
|
||||
TEST_F(CoreHandlerTests, coreShouldSetConnectionErrorOnBrokenConnection) {
|
||||
EXPECT_CALL(http, request(_, _))
|
||||
.WillOnce(Return(RawHttpResponse{_res}.withTimeoutError()));
|
||||
EXPECT_THAT(sut.request("", doc), //
|
||||
AllOf(Not(HasValue()), Unexpected(HttpError{})));
|
||||
EXPECT_CALL(http, request(_, _)).WillOnce(Return(RawHttpResponse{_res}.withTimeoutError()));
|
||||
EXPECT_THAT(sut.request("", doc), //
|
||||
AllOf(Not(HasValue()), Unexpected(ConnectionError{})));
|
||||
}
|
||||
|
||||
TEST_F(CoreHandlerTests,
|
||||
coreShouldCheckSignatureAndReturnBadSignatureBeforeAnythingElse) {
|
||||
EXPECT_CALL(http, request(_, _))
|
||||
.WillOnce(Return(RawHttpResponse{_res}
|
||||
.initMessage()
|
||||
.withBrokenBody()
|
||||
.withBrokenSignature()));
|
||||
EXPECT_THAT(sut.request("", doc), //
|
||||
AllOf(Not(HasValue()), Unexpected(CoreHandlerError{
|
||||
CoreHandlerError::BadSigature})));
|
||||
TEST_F(CoreHandlerTests, coreShouldCheckSignatureAndReturnBadSignatureBeforeAnythingElse) {
|
||||
EXPECT_CALL(http, request(_, _)).WillOnce(Return(RawHttpResponse{_res}.initMessage().withBrokenBody().withBrokenSignature()));
|
||||
EXPECT_THAT(sut.request("", doc), //
|
||||
AllOf(Not(HasValue()), Unexpected(CoreHandlerError{CoreHandlerError::BadSigature})));
|
||||
}
|
||||
|
||||
TEST_F(CoreHandlerTests, coreShouldSetBrokenDataWhenResultIsNotAvailable) {
|
||||
EXPECT_CALL(http, request(_, _))
|
||||
.WillOnce(Return(RawHttpResponse{_res}.initMessage().withBrokenBody()));
|
||||
EXPECT_THAT(sut.request("", doc), //
|
||||
AllOf(Not(HasValue()), Unexpected(CoreHandlerError{
|
||||
CoreHandlerError::BrokenData})));
|
||||
EXPECT_CALL(http, request(_, _)).WillOnce(Return(RawHttpResponse{_res}.initMessage().withBrokenBody()));
|
||||
EXPECT_THAT(sut.request("", doc), //
|
||||
AllOf(Not(HasValue()), Unexpected(CoreHandlerError{CoreHandlerError::BrokenData})));
|
||||
}
|
||||
|
||||
TEST_F(CoreHandlerTests, coreSignatureIsBeingChecked) {
|
||||
EXPECT_CALL(http, request(Eq(conf.parameters.apiServer + "/path"), _))
|
||||
.WillOnce(Return(RawHttpResponse{_res}.initMessage()));
|
||||
EXPECT_THAT(sut.request("/path", doc), //
|
||||
AllOf(HasValue(), IsObject(), HasKey("/result/tid")));
|
||||
EXPECT_CALL(http, request(Eq(std::string{conf.apiServer.data()} + "/path"), _)).WillOnce(Return(RawHttpResponse{_res}.initMessage()));
|
||||
EXPECT_THAT(sut.request("/path", doc), //
|
||||
AllOf(HasValue(), IsObject(), HasKey("/result/tid")));
|
||||
}
|
||||
|
||||
TEST_F(CoreHandlerTests, onHttpProblemsAccessShouldBeDeniedByDefault) {
|
||||
EXPECT_CALL(http, request(_, _))
|
||||
.WillOnce(Return(
|
||||
RawHttpResponse{_res}.initMessage().withServiceUnavailableError()));
|
||||
EXPECT_THAT(sut.request("/path", doc), //
|
||||
AllOf(Not(HasValue()), Unexpected(PamDeny{})));
|
||||
EXPECT_CALL(http, request(_, _)).WillOnce(Return(RawHttpResponse{_res}.initMessage().withServiceUnavailableError()));
|
||||
EXPECT_THAT(sut.request("/path", doc), //
|
||||
AllOf(Not(HasValue()), Unexpected(RublonAuthenticationInterrupt{})));
|
||||
}
|
||||
|
||||
TEST_F(CoreHandlerTests, onSuccessfullMessageCoreShouldByFine) {
|
||||
EXPECT_CALL(http, request(_, _))
|
||||
.WillOnce(
|
||||
Return(RawHttpResponse{_res}.authenticationSuccessfullMessage()));
|
||||
EXPECT_THAT(sut.request("/path", doc), //
|
||||
AllOf(HasValue()));
|
||||
EXPECT_CALL(http, request(_, _)).WillOnce(Return(RawHttpResponse{_res}.authenticationSuccessfullMessage()));
|
||||
EXPECT_THAT(sut.request("/path", doc), AllOf(HasValue()));
|
||||
}
|
||||
|
||||
class CoreHandlerWithBypassTests : public testing::Test {
|
||||
public:
|
||||
CoreHandlerWithBypassTests() : sut{confBypass}, http{sut._http()} {}
|
||||
public:
|
||||
CoreHandlerWithBypassTests() : sut{confBypass}, http{sut._http()} {}
|
||||
|
||||
CoreHandlerTestable sut;
|
||||
rublon::Response _res{memory::default_resource()};
|
||||
HttpHandlerMock &http;
|
||||
CoreHandlerTestable sut;
|
||||
rublon::Response _res{memory::default_resource()};
|
||||
HttpHandlerMock & http;
|
||||
};
|
||||
|
||||
TEST_F(CoreHandlerWithBypassTests,
|
||||
onHttpProblemsAccessShouldBeBypassedWhenEnabled) {
|
||||
EXPECT_CALL(http, request(_, _))
|
||||
.WillOnce(Return(
|
||||
RawHttpResponse{_res}.initMessage().withServiceUnavailableError()));
|
||||
EXPECT_THAT(sut.request("/path", Document{}), //
|
||||
AllOf(Not(HasValue()), Unexpected(PamBaypass{})));
|
||||
TEST_F(CoreHandlerWithBypassTests, onHttpProblemsAccessShouldBeBypassedWhenEnabled) {
|
||||
EXPECT_CALL(http, request(_, _)).WillOnce(Return(RawHttpResponse{_res}.initMessage().withServiceUnavailableError()));
|
||||
EXPECT_THAT(sut.request("/path", Document{}), //
|
||||
AllOf(Not(HasValue()), Unexpected(RublonAuthenticationInterrupt{})));
|
||||
}
|
||||
|
||||
@ -21,11 +21,11 @@ MATCHER(IsObject, "") {
|
||||
}
|
||||
|
||||
MATCHER(IsPAMBaypass,""){
|
||||
return arg.error().category() == rublon::Error::k_PamBaypass;
|
||||
return arg.error().category() == rublon::Error::k_RublonAuthenticationInterrupt;
|
||||
}
|
||||
|
||||
MATCHER(IsPAMDeny,""){
|
||||
return arg.error().category() == rublon::Error::k_PamDeny;
|
||||
return arg.error().category() == rublon::Error::k_RublonAuthenticationInterrupt;
|
||||
}
|
||||
|
||||
MATCHER_P(HasKey, key, "") {
|
||||
|
||||
@ -12,20 +12,20 @@
|
||||
#include "rublon/sign.hpp"
|
||||
|
||||
namespace {
|
||||
rublon::Configuration conf{rublon::Configuration::Parameters{//
|
||||
"320BAB778C4D4262B54CD243CDEFFAFD",
|
||||
"39e8d771d83a2ed3cc728811911c25",
|
||||
"https://staging-core.rublon.net",
|
||||
rublon::Configuration conf{rublon::Configuration{//
|
||||
{"320BAB778C4D4262B54CD243CDEFFAFD"},
|
||||
{"39e8d771d83a2ed3cc728811911c25"},
|
||||
{"https://staging-core.rublon.net"},
|
||||
1,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true}};
|
||||
rublon::Configuration confBypass{rublon::Configuration::Parameters{//
|
||||
"320BAB778C4D4262B54CD243CDEFFAFD",
|
||||
"39e8d771d83a2ed3cc728811911c25",
|
||||
"https://staging-core.rublon.net",
|
||||
rublon::Configuration confBypass{rublon::Configuration{//
|
||||
{"320BAB778C4D4262B54CD243CDEFFAFD"},
|
||||
{"39e8d771d83a2ed3cc728811911c25"},
|
||||
{"https://staging-core.rublon.net"},
|
||||
1,
|
||||
true,
|
||||
true,
|
||||
@ -63,8 +63,8 @@ struct ResponseMockOptions {
|
||||
|
||||
std::string coreException{};
|
||||
std::set< std::string > methods{};
|
||||
|
||||
rublon::Error error;
|
||||
|
||||
std::optional<rublon::Error> error;
|
||||
};
|
||||
|
||||
class InitCoreResponse {
|
||||
@ -186,12 +186,12 @@ class ResponseBase {
|
||||
}
|
||||
|
||||
Generator & withTimeoutError() {
|
||||
options.error = rublon::Error{rublon::HttpError{}};
|
||||
options.error = rublon::Error{rublon::ConnectionError{}};
|
||||
return static_cast< Generator & >(*this);
|
||||
}
|
||||
|
||||
Generator & withServiceUnavailableError() {
|
||||
options.error = rublon::Error{rublon::HttpError{rublon::HttpError::Error, 405}};
|
||||
options.error = rublon::Error{rublon::ConnectionError{rublon::ConnectionError::Error, 405}};
|
||||
return static_cast< Generator & >(*this);
|
||||
}
|
||||
|
||||
@ -212,11 +212,11 @@ class ResponseBase {
|
||||
}
|
||||
|
||||
operator auto() {
|
||||
return options.error.category() == rublon::Error::k_None ? static_cast< Generator * >(this)->generate() : tl::unexpected{error()};
|
||||
return not options.error.has_value() ? static_cast< Generator * >(this)->generate() : tl::unexpected{error()};
|
||||
}
|
||||
|
||||
rublon::Error error() {
|
||||
return rublon::Error{options.error};
|
||||
return rublon::Error{options.error.value()};
|
||||
}
|
||||
|
||||
std::variant< InitCoreResponse, MethodSelectCoreResponse, CodeVerificationResponse, AuthenticationOkResponse > _coreGenerator;
|
||||
@ -245,7 +245,7 @@ class RawHttpResponse : public ResponseBase< RawHttpResponse > {
|
||||
};
|
||||
static auto signResponse(rublon::Response & res, ResponseMockOptions opts) {
|
||||
const auto & sign =
|
||||
opts.generateBrokenSigneture ? std::array< char, 65 >{} : rublon::signData(res.body, conf.parameters.secretKey.c_str());
|
||||
opts.generateBrokenSigneture ? std::array< char, 65 >{} : rublon::signData(res.body, conf.secretKey.data());
|
||||
res.headers["x-rublon-signature"] = sign.data();
|
||||
}
|
||||
tl::expected< std::reference_wrapper< rublon::Response >, rublon::Error > generate() {
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
if [ ! -f /etc/rublon/rublon.config ]
|
||||
if [ ! -f /etc/rublon.config ]
|
||||
then
|
||||
mkdir -p /etc/rublon
|
||||
cp -a /usr/share/rublon/rublon.config.defaults /etc/rublon/rublon.config
|
||||
cp -a /usr/share/rublon/rublon.config.defaults /etc/rublon.config
|
||||
fi
|
||||
|
||||
# get system id
|
||||
@ -23,5 +23,5 @@ grep -qe "^UsePAM" $SSHD_CONF && \
|
||||
sed -i 's/^#*UsePAM[[:space:]]\+.*/UsePAM yes/' $SSHD_CONF || \
|
||||
echo "UsePAM yes" >> $SSHD_CONF
|
||||
|
||||
grep -qe 'auth required pam_rublon.so' $SSHD_PAM_CONF || sed -i '\$aauth required pam_rublon.so' $SSHD_PAM_CONF
|
||||
grep -qe 'account required pam_rublon.so' $SSHD_PAM_CONF || sed -i '\$aaccount required pam_rublon.so' $SSHD_PAM_CONF
|
||||
grep -qe 'auth required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aauth required pam_rublon.so' $SSHD_PAM_CONF
|
||||
grep -qe 'account required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aaccount required pam_rublon.so' $SSHD_PAM_CONF
|
||||
|
||||
Loading…
Reference in New Issue
Block a user