From d035219202e0dd228ec1b90c6f05512f863b6a18 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Mon, 7 Aug 2023 11:40:38 +0200 Subject: [PATCH] Remove template PAM from class defs --- PAM/ssh/CMakeLists.txt | 1 - .../rublon/authentication_step_interface.hpp | 20 +- PAM/ssh/include/rublon/core_handler.hpp | 10 +- PAM/ssh/include/rublon/curl.hpp | 2 +- PAM/ssh/include/rublon/init.hpp | 42 +-- PAM/ssh/include/rublon/json.hpp | 17 +- PAM/ssh/include/rublon/method/OTP.hpp | 54 ++- PAM/ssh/include/rublon/method/SMS.hpp | 39 ++- .../include/rublon/method/method_factory.hpp | 99 ++---- PAM/ssh/include/rublon/pam.hpp | 5 +- PAM/ssh/include/rublon/pam_action.hpp | 9 +- PAM/ssh/include/rublon/rublon.hpp | 3 - PAM/ssh/include/rublon/span.hpp | 329 ------------------ PAM/ssh/lib/pam.cpp | 38 +- PAM/ssh/tests/CMakeLists.txt | 8 +- .../authentication_step_common_tests.cpp | 4 + PAM/ssh/tests/init_test.cpp | 47 ++- 17 files changed, 201 insertions(+), 526 deletions(-) delete mode 100644 PAM/ssh/include/rublon/span.hpp create mode 100644 PAM/ssh/tests/authentication_step_common_tests.cpp diff --git a/PAM/ssh/CMakeLists.txt b/PAM/ssh/CMakeLists.txt index f13a2a0..fab0de9 100644 --- a/PAM/ssh/CMakeLists.txt +++ b/PAM/ssh/CMakeLists.txt @@ -21,7 +21,6 @@ if(${CMAKE_VERSION} VERSION_GREATER "3.19.0") ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/pam.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/rublon.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/sign.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/span.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/utils.hpp ) endif() diff --git a/PAM/ssh/include/rublon/authentication_step_interface.hpp b/PAM/ssh/include/rublon/authentication_step_interface.hpp index d33d0a0..22062b7 100644 --- a/PAM/ssh/include/rublon/authentication_step_interface.hpp +++ b/PAM/ssh/include/rublon/authentication_step_interface.hpp @@ -1,5 +1,6 @@ #pragma once +#include "rublon/pam.hpp" #include "rublon/pam_action.hpp" #include #include @@ -13,16 +14,30 @@ class AuthenticationStep { std::string _tid; public: - AuthenticationStep(){} + AuthenticationStep() {} AuthenticationStep(std::string systemToken, std::string tid) : _systemToken{std::move(systemToken)}, _tid{std::move(tid)} {} template < typename Handler_t > auto fire(const CoreHandlerInterface< Handler_t > & coreHandler) const { - // log step log(Info, "Starting %s step", static_cast< const Impl * >(this)->name); return static_cast< const Impl * >(this)->handle(coreHandler); } + template < typename Handler_t, typename PamInfo_t = LinuxPam > + auto fire(const CoreHandlerInterface< Handler_t > & coreHandler, const PamInfo_t & pam) const { + log(Info, "Starting %s step", static_cast< const Impl * >(this)->name); + return static_cast< const Impl * >(this)->handle(coreHandler, pam); + } + + protected: + void addSystemToken(Document & body, RapidJSONPMRAlloc & alloc) const { + body.AddMember("systemToken", Value{this->_systemToken.c_str(), alloc}, alloc); + } + + void addTid(Document & body, RapidJSONPMRAlloc & alloc) const { + body.AddMember("tid", Value{this->_tid.c_str(), alloc}, alloc); + } + template < typename HandlerReturn_t > PamAction coreErrorHandler(const HandlerReturn_t & coreResponse) const { switch(coreResponse.error().errorClass) { @@ -39,6 +54,7 @@ class AuthenticationStep { log(LogLevel::Error, "ErrorClass::BrokenData"); return PamAction::decline; } + return PamAction::decline; } }; diff --git a/PAM/ssh/include/rublon/core_handler.hpp b/PAM/ssh/include/rublon/core_handler.hpp index aa684ec..69124ff 100644 --- a/PAM/ssh/include/rublon/core_handler.hpp +++ b/PAM/ssh/include/rublon/core_handler.hpp @@ -14,8 +14,6 @@ namespace rublon { - - template < typename HttpHandler = CURL > class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { std::string secretKey; @@ -49,12 +47,12 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { std::byte _buffer[16 * 1024]; std::pmr::monotonic_buffer_resource mr{_buffer, sizeof(_buffer)}; rublon::RapidJSONPMRAlloc alloc{&mr}; - + rublon::StringBuffer jsonStr{&alloc}; rublon::Writer writer{jsonStr, &alloc}; - + body.Accept(writer); - + Request request; request.headers["Content-Type"] = "application/json"; request.headers["Accept"] = "application/json"; @@ -70,7 +68,7 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { log(LogLevel::Error, "CoreHandlerError::ConnectionError"); return tl::unexpected{CoreHandlerError::ConnectionError}; } - + if(not responseSigned(*response)) { log(LogLevel::Error, "CoreHandlerError::BadSigature"); return tl::unexpected{CoreHandlerError::BadSigature}; diff --git a/PAM/ssh/include/rublon/curl.hpp b/PAM/ssh/include/rublon/curl.hpp index 61d0113..cebb548 100644 --- a/PAM/ssh/include/rublon/curl.hpp +++ b/PAM/ssh/include/rublon/curl.hpp @@ -60,7 +60,7 @@ class CURL { curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response_data); - log(Info, "Request send uri:%s body:\n%s\n", uri.data(), request.body.c_str()); + log(Debug, "Request send uri:%s body:\n%s\n", uri.data(), request.body.c_str()); auto res = curl_easy_perform(curl.get()); if(res != CURLE_OK) { log(Error, "No response from Rublon server err:{%s}", curl_easy_strerror(res)); diff --git a/PAM/ssh/include/rublon/init.hpp b/PAM/ssh/include/rublon/init.hpp index 22990cd..712bec1 100644 --- a/PAM/ssh/include/rublon/init.hpp +++ b/PAM/ssh/include/rublon/init.hpp @@ -8,41 +8,35 @@ #include -namespace rublon{ -class Verify{}; -} +namespace rublon { +class Verify {}; +} // namespace rublon namespace rublon { -template < class MethodSelect_t = MethodSelect, typename Pam_t = LinuxPam > -class Init : public AuthenticationStep< Init< MethodSelect_t, Pam_t > > { - using base_t = AuthenticationStep< Init< MethodSelect_t, Pam_t > >; - - const char * apiPath = "/api/transaction/init"; +template < class MethodSelect_t = MethodSelect > +class Init : public AuthenticationStep< Init< MethodSelect_t > > { + using base_t = AuthenticationStep< Init< MethodSelect_t > >; - protected: - Pam_t & _pamInfo; + const char * apiPath = "/api/transaction/init"; public: const char * name = "Initialization"; - - Init(Pam_t & pamHandler, const rublon::Configuration & config) - : base_t(config.parameters.systemToken, ""), _pamInfo{pamHandler} {} + + Init(const rublon::Configuration & config) : base_t(config.parameters.systemToken, "") {} /// TODO add core handler interface - template < typename Hander_t > - tl::expected< MethodSelect_t, PamAction > handle(const CoreHandlerInterface< Hander_t > & coreHandler) const { - char _buffer[1024]; - std::pmr::monotonic_buffer_resource mr{_buffer, 1024}; - - RapidJSONPMRAlloc alloc{&mr}; + template < typename Hander_t, typename PamInfo_t = LinuxPam > + tl::expected< MethodSelect_t, PamAction > handle(const CoreHandlerInterface< Hander_t > & coreHandler, const PamInfo_t & pam) const { + RapidJSONPMRStackAlloc< 1024 > alloc{}; Document body{rapidjson::kObjectType, &alloc}; - - body.AddMember("systemToken", Value{this->_systemToken.c_str(), alloc}, alloc); - body.AddMember("username", Value{this->_pamInfo.username().get(), alloc}, alloc); + + this->addSystemToken(body, alloc); + + body.AddMember("username", Value{pam.username().get(), alloc}, alloc); body.AddMember("userEmail", "bwi@rublon.com", alloc); /// TODO proper username Value params{rapidjson::kObjectType}; - params.AddMember("userIP", Value{_pamInfo.ip().get(), alloc}, alloc); + params.AddMember("userIP", Value{pam.ip().get(), alloc}, alloc); params.AddMember("appVer", "v.1.6", alloc); /// TODO add version to cmake params.AddMember("os", "Ubuntu 23.04", alloc); /// TODO add version to cmake @@ -58,8 +52,6 @@ class Init : public AuthenticationStep< Init< MethodSelect_t, Pam_t > > { } else { return tl::unexpected{this->coreErrorHandler(coreResponse)}; } - - return tl::unexpected{PamAction::decline}; } }; } // namespace rublon diff --git a/PAM/ssh/include/rublon/json.hpp b/PAM/ssh/include/rublon/json.hpp index 5b14980..77cdc8b 100644 --- a/PAM/ssh/include/rublon/json.hpp +++ b/PAM/ssh/include/rublon/json.hpp @@ -13,6 +13,7 @@ struct RapidJSONPMRAlloc { static constexpr auto objectOffset = alignof(std::max_align_t); static constexpr auto memPadding = objectOffset * 2; + RapidJSONPMRAlloc(std::pmr::memory_resource * mr = std::pmr::get_default_resource()) : upstream{mr} {} void * Malloc(size_t size) { if(size != 0) { const auto allocated_size = size + memPadding; @@ -69,9 +70,19 @@ struct RapidJSONPMRAlloc { } }; -using Document = rapidjson::GenericDocument< rapidjson::UTF8<>, RapidJSONPMRAlloc >; -using Value = rapidjson::GenericValue< rapidjson::UTF8<>, RapidJSONPMRAlloc >; +template < std::size_t N > +struct RapidJSONPMRStackAlloc : public RapidJSONPMRAlloc { + private: + char _buffer[N]; + std::pmr::monotonic_buffer_resource mr{_buffer, N}; + + public: + RapidJSONPMRStackAlloc() : RapidJSONPMRAlloc(&mr) {} +}; + +using Document = rapidjson::GenericDocument< rapidjson::UTF8<>, RapidJSONPMRAlloc >; +using Value = rapidjson::GenericValue< rapidjson::UTF8<>, RapidJSONPMRAlloc >; using StringBuffer = rapidjson::GenericStringBuffer< rapidjson::UTF8<>, RapidJSONPMRAlloc >; -using Writer = rapidjson::Writer< StringBuffer, rapidjson::UTF8<>, rapidjson::UTF8<>, RapidJSONPMRAlloc >; +using Writer = rapidjson::Writer< StringBuffer, rapidjson::UTF8<>, rapidjson::UTF8<>, RapidJSONPMRAlloc >; } // namespace rublon diff --git a/PAM/ssh/include/rublon/method/OTP.hpp b/PAM/ssh/include/rublon/method/OTP.hpp index eb31f10..e77a95f 100644 --- a/PAM/ssh/include/rublon/method/OTP.hpp +++ b/PAM/ssh/include/rublon/method/OTP.hpp @@ -6,54 +6,40 @@ #include #include -namespace rublon { -class Confirmation {}; -}; // namespace rublon - namespace rublon::method { -template < typename PamInfo_t = LinuxPam > -class OTP : public AuthenticationStep< OTP< PamInfo_t > > { - using base_t = AuthenticationStep< OTP< PamInfo_t > >; - const char * uri = "/api/transaction/confirmCode"; - - protected: - const PamInfo_t & _pamInfo; +class OTP : public AuthenticationStep< OTP > { + using base_t = AuthenticationStep< OTP >; + const char * uri = "/api/transaction/confirmCode"; public: - const char * name = "One Time Password"; + const char * name = "TOTP"; - OTP(std::string systemToken, std::string tid, const PamInfo_t & pam) : base_t(std::move(systemToken), std::move(tid)), _pamInfo{pam} {} - - template < typename Hander_t > - tl::expected< Confirmation, PamAction > handle(const CoreHandlerInterface< Hander_t > & coreHandler) const { - log(Debug, "OTP fired"); - - const auto passcode = - _pamInfo.scan([](const char * userInput) { return std::string{userInput}; }, "Mobile TOTP from Rublon Authenticator:"); - - char _buffer[1024]; - std::pmr::monotonic_buffer_resource mr{_buffer, 1024}; - - RapidJSONPMRAlloc alloc{&mr}; + OTP(std::string systemToken, std::string tid) : base_t(std::move(systemToken), std::move(tid)) {} + + template < typename Hander_t, typename PamInfo_t = LinuxPam > + tl::expected< AuthenticationStatus, PamAction > handle(const CoreHandlerInterface< Hander_t > & coreHandler, + const PamInfo_t & pam) const { + RapidJSONPMRStackAlloc< 1024 > alloc{}; Document body{rapidjson::kObjectType, &alloc}; - - body.AddMember("systemToken", Value{this->_systemToken.c_str(), alloc}, alloc); - body.AddMember("tid", Value{this->_tid.c_str(), alloc}, alloc); + + this->addSystemToken(body, alloc); + this->addTid(body, alloc); + + const auto passcode = + pam.scan([](const char * userInput) { return std::string{userInput}; }, "Mobile TOTP from Rublon Authenticator:"); + body.AddMember("vericode", Value{passcode.value().c_str(), alloc}, alloc); /// TODO proper username - + auto coreResponse = coreHandler.request(uri, body); - + if(coreResponse.has_value()) { - log(LogLevel::Info, "[TMP] has response, processing", __PRETTY_FUNCTION__); const auto & rublonResponse = coreResponse.value()["result"]; - std::string tid = rublonResponse["tid"].GetString(); + // {"status":"OK","result":true} return tl::unexpected{PamAction::accept}; } else { return tl::unexpected{this->coreErrorHandler(coreResponse)}; } - - return tl::unexpected{PamAction::accept}; } }; diff --git a/PAM/ssh/include/rublon/method/SMS.hpp b/PAM/ssh/include/rublon/method/SMS.hpp index cfb7dda..fd358d3 100644 --- a/PAM/ssh/include/rublon/method/SMS.hpp +++ b/PAM/ssh/include/rublon/method/SMS.hpp @@ -1,8 +1,45 @@ #pragma once +#include + +#include +#include +#include + namespace rublon::method { -class SMS { + +class SMS : public AuthenticationStep< SMS > { + using base_t = AuthenticationStep< SMS >; + const char * uri = "/api/transaction/confirmCode"; + public: const char * name = "SMS"; + + SMS(std::string systemToken, std::string tid) : base_t(std::move(systemToken), std::move(tid)) {} + + template < typename Hander_t, typename PamInfo_t = LinuxPam > + tl::expected< AuthenticationStatus, PamAction > handle(const CoreHandlerInterface< Hander_t > & coreHandler, + const PamInfo_t & pam) const { + RapidJSONPMRStackAlloc< 1024 > alloc{}; + Document body{rapidjson::kObjectType, &alloc}; + + this->addSystemToken(body, alloc); + this->addTid(body, alloc); + + const auto passcode = pam.scan([](const char * userInput) { return std::string{userInput}; }, "SMS passcode:"); + + body.AddMember("vericode", Value{passcode.value().c_str(), alloc}, alloc); + + auto coreResponse = coreHandler.request(uri, body); + + if(coreResponse.has_value()) { + const auto & rublonResponse = coreResponse.value()["result"]; + // {"status":"OK","result":true} + return tl::unexpected{PamAction::accept}; + } else { + return tl::unexpected{this->coreErrorHandler(coreResponse)}; + } + } }; + } // namespace rublon::method diff --git a/PAM/ssh/include/rublon/method/method_factory.hpp b/PAM/ssh/include/rublon/method/method_factory.hpp index b061587..18b8494 100644 --- a/PAM/ssh/include/rublon/method/method_factory.hpp +++ b/PAM/ssh/include/rublon/method/method_factory.hpp @@ -13,93 +13,51 @@ namespace rublon { -template < typename Pam_t = LinuxPam > -class Method : AuthenticationStep< Method< Pam_t > > { - std::string _systemToken; - std::string _tid; +class Method : public AuthenticationStep< Method > { + using base_t = AuthenticationStep< Method >; public: template < typename Method_t > - Method(Method_t && metho, std::string systemToken, std::string tid) : _impl{metho}, _systemToken{systemToken}, _tid{tid} {} - - template < typename Handler_t > - tl::expected< Confirmation, PamAction > fire(const CoreHandlerInterface< Handler_t > & coreHandler) const { - log(Debug, "method select"); - - char _buffer[1024]; - std::pmr::monotonic_buffer_resource mr{_buffer, 1024}; - - RapidJSONPMRAlloc alloc{&mr}; - Document body{rapidjson::kObjectType, &alloc}; - - body.AddMember("systemToken", Value{_systemToken.c_str(), alloc}, alloc); - body.AddMember("tid", Value{_tid.c_str(), alloc}, alloc); - body.AddMember("method", Value{"totp", alloc}, alloc); - body.AddMember("GDPRAccepted", Value{"true", alloc}, alloc); - body.AddMember("tosAccepted", Value{"true", alloc}, alloc); - - auto coreResponse = coreHandler.request("/api/transaction/methodSSH", body); - - if(coreResponse.has_value()) { - log(LogLevel::Info, "[TMP] has response, processing", __PRETTY_FUNCTION__); - const auto & rublonResponse = coreResponse.value()["result"]; - std::string tid = rublonResponse["tid"].GetString(); - return tl::unexpected{PamAction::accept}; - } else { - // mostly connectio errors - switch(coreResponse.error().errorClass) { - case CoreHandlerError::ErrorClass::BadSigature: - log(LogLevel::Error, "ErrorClass::BadSigature"); - return tl::unexpected{PamAction::decline}; - case CoreHandlerError::ErrorClass::CoreException: /// TODO exception handling - log(LogLevel::Error, "ErrorClass::CoreException"); - return tl::unexpected{PamAction::decline}; /// TODO accept? - case CoreHandlerError::ErrorClass::ConnectionError: - log(LogLevel::Error, "ErrorClass::ConnectionError"); - return tl::unexpected{PamAction::decline}; /// TODO decline? - case CoreHandlerError::ErrorClass::BrokenData: - log(LogLevel::Error, "ErrorClass::BrokenData"); - return tl::unexpected{PamAction::decline}; - } - } + Method(Method_t method, std::string systemToken, std::string tid) : base_t(std::move(systemToken), std::move(tid)), _impl{method} {} + template < typename Handler_t, typename PamInfo_t = LinuxPam > + tl::expected< AuthenticationStatus, PamAction > fire(const CoreHandlerInterface< Handler_t > & coreHandler, + const PamInfo_t & pam) const { return std::visit( [&](const auto & method) { rublon::log(LogLevel::Info, "Using '%s' method", method.name); - return method.fire(coreHandler); + return method.fire(coreHandler, pam); }, _impl); } private: - std::variant< method::OTP< Pam_t > > _impl; + std::variant< method::OTP, method::SMS > _impl; }; -template < typename Pam_t = LinuxPam > -class PostMethod : public rublon::AuthenticationStep< PostMethod< Pam_t > > { - using base_t = rublon::AuthenticationStep< PostMethod< Pam_t > >; - - const char * uri = "/api/transaction/methodSSH"; +class PostMethod : public rublon::AuthenticationStep< PostMethod > { + using base_t = rublon::AuthenticationStep< PostMethod >; + + const char * uri = "/api/transaction/methodSSH"; std::string _method; - + public: const char * name = "Confirm Method"; - + PostMethod(std::string systemToken, std::string tid, std::string method) : base_t(std::move(systemToken), std::move(tid)), _method{method} {} - template < typename Hander_t > - tl::expected< Method<>, PamAction > handle(const CoreHandlerInterface< Hander_t > & coreHandler) const { - char _buffer[1024]; - std::pmr::monotonic_buffer_resource mr{_buffer, 1024}; - RapidJSONPMRAlloc alloc{&mr}; + template < typename Hander_t, typename PamInfo_t = LinuxPam > + tl::expected< Method, PamAction > handle(const CoreHandlerInterface< Hander_t > & coreHandler) const { + RapidJSONPMRStackAlloc< 1024 > alloc{}; Document body{rapidjson::kObjectType, &alloc}; - - body.AddMember("systemToken", Value{this->_systemToken.c_str(), alloc}, alloc); - body.AddMember("tid", Value{this->_tid.c_str(), alloc}, alloc); + + this->addSystemToken(body, alloc); + this->addTid(body, alloc); + body.AddMember("method", Value{_method.c_str(), alloc}, alloc); - body.AddMember("GDPRAccepted", Value{"true", alloc}, alloc); - body.AddMember("tosAccepted", Value{"true", alloc}, alloc); + body.AddMember("GDPRAccepted", "true", alloc); + body.AddMember("tosAccepted", "true", alloc); auto coreResponse = coreHandler.request(uri, body); @@ -107,7 +65,12 @@ class PostMethod : public rublon::AuthenticationStep< PostMethod< Pam_t > > { log(LogLevel::Info, "[TMP] has response, processing", __PRETTY_FUNCTION__); const auto & rublonResponse = coreResponse.value()["result"]; std::string tid = rublonResponse["tid"].GetString(); - return tl::unexpected{PamAction::accept}; + + if(_method == "totp") { + return Method{method::OTP{this->_systemToken, this->_tid}, this->_systemToken, this->_tid}; + } else if(_method == "sms") { + return Method{method::SMS{this->_systemToken, this->_tid}, this->_systemToken, this->_tid}; + } } else { return tl::unexpected{this->coreErrorHandler(coreResponse)}; } @@ -131,7 +94,7 @@ class MethodSelect { } template < typename Pam_t > - tl::expected< PostMethod< Pam_t >, PamAction > create(const Pam_t & pam, const std::string & tid) const { + tl::expected< PostMethod, PamAction > create(Pam_t & pam) const { std::pmr::map< int, std::string > methods_id; pam.print("%s", ""); int i{}; @@ -171,7 +134,7 @@ class MethodSelect { "you selected: %s", methods_id.count(methodid.value_or(0)) ? methods_id.at(methodid.value_or(0)).c_str() : "unknown option"); if(methods_id.at(methodid.value_or(0)) == "totp") { - return PostMethod< Pam_t >{systemToken, tid, methods_id.at(methodid.value_or(0))}; + return PostMethod{systemToken, _tid, methods_id.at(methodid.value_or(0))}; } return tl::unexpected{PamAction::accept}; diff --git a/PAM/ssh/include/rublon/pam.hpp b/PAM/ssh/include/rublon/pam.hpp index 5047db6..b9dd305 100644 --- a/PAM/ssh/include/rublon/pam.hpp +++ b/PAM/ssh/include/rublon/pam.hpp @@ -49,9 +49,9 @@ class LinuxPam { void print(const char * fmt, Ti... ti) const noexcept { pam_prompt(pamh, PAM_TEXT_INFO, nullptr, fmt, std::forward< Ti >(ti)...); } - + template < typename Fun, typename... Ti > - [[nodiscard]] auto scan(Fun && f, const char * fmt, Ti... ti) const noexcept { + auto scan(Fun && f, const char * fmt, Ti... ti) const noexcept { char * responseBuffer = nullptr; pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &responseBuffer, fmt, std::forward< Ti >(ti)...); if(responseBuffer) { @@ -61,5 +61,6 @@ class LinuxPam { } return std::optional< std::result_of_t< Fun(char *) > >(); } + }; } // namespace rublon diff --git a/PAM/ssh/include/rublon/pam_action.hpp b/PAM/ssh/include/rublon/pam_action.hpp index ee79e51..7281706 100644 --- a/PAM/ssh/include/rublon/pam_action.hpp +++ b/PAM/ssh/include/rublon/pam_action.hpp @@ -1,5 +1,10 @@ #pragma once -namespace rublon{ +namespace rublon { + enum class PamAction { accept, decline }; -} + +class AuthenticationStatus {}; + + +} // namespace rublon diff --git a/PAM/ssh/include/rublon/rublon.hpp b/PAM/ssh/include/rublon/rublon.hpp index 7d7ca0e..32fcacf 100644 --- a/PAM/ssh/include/rublon/rublon.hpp +++ b/PAM/ssh/include/rublon/rublon.hpp @@ -12,9 +12,6 @@ #include #include - - -#include #include #include diff --git a/PAM/ssh/include/rublon/span.hpp b/PAM/ssh/include/rublon/span.hpp deleted file mode 100644 index ac176e9..0000000 --- a/PAM/ssh/include/rublon/span.hpp +++ /dev/null @@ -1,329 +0,0 @@ -#pragma once - -#include // for std::array, etc. -#include // for assert -#include // for std::size_t, etc. -#include // for std::reverse_iterator, etc. -#include // for std::enable_if, etc. - -namespace rublon { - -#define CONSTRAINT(...) std::enable_if_t< (__VA_ARGS__), int > = 0 -#define EXPECTS(...) assert((__VA_ARGS__)) - -// constants - -// equivalent to std::numeric_limits::max() -inline constexpr std::size_t dynamic_extent = -1; - -// class template span - -template < class T, std::size_t N = dynamic_extent > -class span; - -namespace span_detail { - - // detect specializations of span - - template < class T > - struct is_span : std::false_type {}; - - template < class T, std::size_t N > - struct is_span< span< T, N > > : std::true_type {}; - - template < class T > - inline constexpr bool is_span_v = is_span< T >::value; - - // detect specializations of std::array - - template < class T > - struct is_array : std::false_type {}; - - template < class T, std::size_t N > - struct is_array< std::array< T, N > > : std::true_type {}; - - template < class T > - inline constexpr bool is_array_v = is_array< T >::value; - - // ADL-aware data() and size() - - template < class C > - constexpr decltype(auto) my_data(C & c) { - return std::data(c); - } - - template < class C > - constexpr decltype(auto) my_size(C & c) { - return std::size(c); - } - - // detect container - - template < class C, class = void > - struct is_cont : std::false_type {}; - - template < class C > - struct is_cont< C, - std::void_t< std::enable_if_t< !is_span_v< C > >, - std::enable_if_t< !is_array_v< C > >, - std::enable_if_t< !std::is_array_v< C > >, - decltype(std::data(std::declval< C >())), - decltype(std::size(std::declval< C >())) > > : std::true_type {}; - - template < class C > - inline constexpr bool is_cont_v = is_cont< C >::value; -} // namespace span_detail - -template < class T, std::size_t N > -class span { - public: - // constants and types - - using element_type = T; - using value_type = std::remove_cv_t< T >; - using index_type = std::size_t; - using difference_type = std::ptrdiff_t; - - using pointer = T *; - using const_pointer = const T *; - using reference = T &; - using const_reference = const T &; - - using iterator = T *; - using const_iterator = const T *; - using reverse_iterator = std::reverse_iterator< iterator >; - using const_reverse_iterator = std::reverse_iterator< const_iterator >; - - static constexpr index_type extent = N; - - // constructors, copy, and assignment - - // LWG 3198 applied - constexpr span() noexcept : size_{0}, data_{nullptr} { - static_assert(N == dynamic_extent || N == 0); - } - - constexpr span(T * ptr, index_type n) : size_{n}, data_{ptr} { - EXPECTS(N == dynamic_extent || N == n); - } - - constexpr span(T * first, T * last) : size_{last - first}, data_{first} { - EXPECTS(N == dynamic_extent || last - first = N); - } - - template < std::size_t M, - CONSTRAINT(N == dynamic_extent || - N == M && - std::is_convertible_v< std::remove_pointer_t< decltype(span_detail::my_data(std::declval< T (&)[M] >())) > (*)[], T (*)[] >) > - constexpr span(T (&arr)[M]) noexcept : size_{M}, data_{arr} {} - - template < std::size_t M, - CONSTRAINT(N == dynamic_extent || - N == M && - std::is_convertible_v< std::remove_pointer_t< decltype(span_detail::my_data(std::declval< T (&)[M] >())) > (*)[], T (*)[] >) > - constexpr span(std::array< value_type, M > & arr) noexcept : size_{M}, data_{arr.data()} {} - - template < std::size_t M, - CONSTRAINT(N == dynamic_extent || - N == M && - std::is_convertible_v< std::remove_pointer_t< decltype(span_detail::my_data(std::declval< T (&)[M] >())) > (*)[], T (*)[] >) > - constexpr span(const std::array< value_type, M > & arr) noexcept : size_{M}, data_{arr.data()} {} - - template < class Cont, - CONSTRAINT(N == dynamic_extent && span_detail::is_cont_v< Cont > && - std::is_convertible_v< std::remove_pointer_t< decltype(span_detail::my_data(std::declval< Cont >())) > (*)[], T (*)[] >) > - constexpr span(Cont & c) : size_{span_detail::my_size(c)}, data_{span_detail::my_data(c)} {} - - template < class Cont, - CONSTRAINT(N == dynamic_extent && span_detail::is_cont_v< Cont > && - std::is_convertible_v< std::remove_pointer_t< decltype(span_detail::my_data(std::declval< Cont >())) > (*)[], T (*)[] >) > - constexpr span(const Cont & c) : size_{span_detail::my_size(c)}, data_{span_detail::my_data(c)} {} - - constexpr span(const span & other) noexcept = default; - - // template < class U, std::size_t M, CONSTRAINT(N == dynamic_extent || N == M && std::is_convertible_v< U (*)[], T (*)[] >) > - // constexpr span(const span< U, M > & s) noexcept : size_{s.size()}, data_{s.data()} {} - - ~span() noexcept = default; - - constexpr span & operator=(const span & other) noexcept = default; - - // subviews - - template < std::size_t Cnt > - constexpr span< T, Cnt > first() const { - assert(Cnt <= size()); - return {data(), Cnt}; - } - - template < std::size_t Cnt > - constexpr span< T, Cnt > last() const { - assert(Cnt <= size()); - return {data() + (size() - Cnt), Cnt}; - } - - template < std::size_t Off, std::size_t Cnt = dynamic_extent > - constexpr auto subspan() const { - assert(Off <= size() && (Cnt == dynamic_extent || Off + Cnt <= size())); - if constexpr(Cnt != dynamic_extent) - return span< T, Cnt >{data() + Off, Cnt}; - else if constexpr(N != dynamic_extent) - return span< T, N - Off >{data() + Off, size() - Off}; - else - return span< T, dynamic_extent >{data() + Off, size() - Off}; - } - - constexpr span< T, dynamic_extent > first(index_type cnt) const { - assert(cnt <= size()); - return {data(), cnt}; - } - - constexpr span< T, dynamic_extent > last(index_type cnt) const { - assert(cnt <= size()); - return {data() + (size() - cnt), cnt}; - } - - constexpr span< T, dynamic_extent > subspan(index_type off, index_type cnt = dynamic_extent) const { - assert(off <= size() && (cnt == dynamic_extent || off + cnt <= size())); - return {data() + off, cnt == dynamic_extent ? size() - off : cnt}; - } - - // observers - - constexpr index_type size() const noexcept { - return size_; - } - - constexpr index_type size_bytes() const noexcept { - return size() * sizeof(T); - } - - [[nodiscard]] constexpr bool empty() const noexcept { - return size() == 0; - } - - // element access - - constexpr reference operator[](index_type idx) const { - assert(idx < size()); - return *(data() + idx); - } - - constexpr reference front() const { - assert(!empty()); - return *data(); - } - - constexpr reference back() const { - assert(!empty()); - return *(data() + (size() - 1)); - } - - constexpr pointer data() const noexcept { - return data_; - } - - // iterator support - - constexpr iterator begin() const noexcept { - return data(); - } - - constexpr iterator end() const noexcept { - return data() + size(); - } - - constexpr const_iterator cbegin() const noexcept { - return data(); - } - - constexpr const_iterator cend() const noexcept { - return data() + size(); - } - - constexpr reverse_iterator rbegin() const noexcept { - return reverse_iterator{end()}; - } - - constexpr reverse_iterator rend() const noexcept { - return reverse_iterator{begin()}; - } - - constexpr const_reverse_iterator crbegin() const noexcept { - return reverse_iterator{cend()}; - } - - constexpr const_reverse_iterator crend() const noexcept { - return reverse_iterator{cbegin()}; - } - - friend constexpr iterator begin(span s) noexcept { - return s.begin(); - } - - friend constexpr iterator end(span s) noexcept { - return s.end(); - } - - private: - pointer data_; - index_type size_; -}; - -// deduction guide - -template < class T, std::size_t N > -span(T (&)[N]) -> span< T, N >; - -template < class T, std::size_t N > -span(std::array< T, N > &) -> span< T, N >; - -template < class T, std::size_t N > -span(const std::array< T, N > &) -> span< const T, N >; - -template < class Cont > -span(Cont &) -> span< typename Cont::value_type >; - -template < class Cont > -span(const Cont &) -> span< const typename Cont::value_type >; - -// views of objects representation - -template < class T, std::size_t N > -auto as_bytes(span< T, N > s) noexcept -> span< const std::byte, N == dynamic_extent ? dynamic_extent : sizeof(T) * N > { - return {reinterpret_cast< const std::byte * >(s.data()), s.size_bytes()}; -} - -template < class T, std::size_t N, CONSTRAINT(!std::is_const_v< T >) > -auto as_writable_bytes(span< T, N > s) noexcept -> span< std::byte, N == dynamic_extent ? dynamic_extent : sizeof(T) * N > { - return {reinterpret_cast< std::byte * >(s.data()), s.size_bytes()}; -} -} // namespace rublon - -namespace std { - -// tuple interface -// the primary template declarations are included in - -template < class T, std::size_t N > -struct tuple_size< rublon::span< T, N > > : std::integral_constant< std::size_t, N > {}; - -// not defined -template < class T > -struct tuple_size< rublon::span< T, rublon::dynamic_extent > >; - -template < std::size_t I, class T, std::size_t N > -struct tuple_element< I, rublon::span< T, N > > { - static_assert(N != rublon::dynamic_extent && I < N); - using type = T; -}; - -template < std::size_t I, class T, std::size_t N > -constexpr T & get(rublon::span< T, N > s) noexcept { - static_assert(N != rublon::dynamic_extent && I < N); - return s[I]; -} -} // namespace std - -#undef CONSTRAINT -#undef EXPECTS diff --git a/PAM/ssh/lib/pam.cpp b/PAM/ssh/lib/pam.cpp index 0e80de4..a1b267f 100644 --- a/PAM/ssh/lib/pam.cpp +++ b/PAM/ssh/lib/pam.cpp @@ -6,11 +6,10 @@ #include +#include +#include #include #include -#include - -#include #define DLL_PUBLIC __attribute__((visibility("default"))) @@ -39,29 +38,26 @@ 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) { - rublon::log(rublon::LogLevel::Debug, "pam start"); + using namespace rublon; - auto rublonConfig = rublon::ConfigurationFactory{}.systemConfig(); + auto rublonConfig = ConfigurationFactory{}.systemConfig(); - rublon::CoreHandler CH{rublonConfig.value()}; - rublon::LinuxPam pam{pamh}; + CoreHandler CH{rublonConfig.value()}; + LinuxPam pam{pamh}; - auto selectMethod = [&](const rublon::MethodSelect & selector) { return selector.create(pam, std::string{""}); }; - auto confirmMethod = [&](const rublon::PostMethod & confirm) { return confirm.fire(CH); }; + auto selectMethod = [&](const MethodSelect & selector) { return selector.create(pam); }; + auto confirmMethod = [&](const PostMethod & confirm) { return confirm.fire(CH); }; + auto verifi = [&](const Method & method) { return method.fire(CH, pam); }; - auto method = rublon::Init{pam, rublonConfig.value()} - .fire(CH) // - .and_then(selectMethod) - .and_then(confirmMethod); + auto authStatus = Init{rublonConfig.value()} + .fire(CH, pam) // + .and_then(selectMethod) + .and_then(confirmMethod) + .and_then(verifi); - method.value().fire(CH); - - rublon::log(rublon::LogLevel::Debug, "Method"); - - method.value().fire(CH); - - // .and_then([](const auto & value) { return tl::expected< rublon::Configuration, rublon::PamAction >{}; }) // - // .and_then([](const auto & conf) { return tl::expected< rublon::Configuration, rublon::PamAction >{}; }); + if(authStatus.has_value()) { + rublon::log(rublon::Info, "Auth OK"); + } return PAM_SUCCESS; } diff --git a/PAM/ssh/tests/CMakeLists.txt b/PAM/ssh/tests/CMakeLists.txt index d2dd84d..c2b51aa 100644 --- a/PAM/ssh/tests/CMakeLists.txt +++ b/PAM/ssh/tests/CMakeLists.txt @@ -20,5 +20,11 @@ FetchContent_MakeAvailable( googletest googlebenchmark) -add_executable(rublon-tests utilsTests.cpp rublonTests.cpp core_handler_tests.cpp init_test.cpp method_factory_test.cpp) +add_executable(rublon-tests + authentication_step_common_tests.cpp + utilsTests.cpp + rublonTests.cpp + core_handler_tests.cpp + init_test.cpp method_factory_test.cpp +) target_link_libraries(rublon-tests rublon-ssh GTest::gmock_main -lssl -lcrypto) diff --git a/PAM/ssh/tests/authentication_step_common_tests.cpp b/PAM/ssh/tests/authentication_step_common_tests.cpp new file mode 100644 index 0000000..5663347 --- /dev/null +++ b/PAM/ssh/tests/authentication_step_common_tests.cpp @@ -0,0 +1,4 @@ +#include + + +#include diff --git a/PAM/ssh/tests/init_test.cpp b/PAM/ssh/tests/init_test.cpp index a4b7cca..cd85676 100644 --- a/PAM/ssh/tests/init_test.cpp +++ b/PAM/ssh/tests/init_test.cpp @@ -27,12 +27,12 @@ class CoreHandlerMock : public CoreHandlerInterface< CoreHandlerMock > { gen.generateBrokenData = true; return *this; } - - CoreHandlerMock & methods(std::initializer_list methods) { + + CoreHandlerMock & methods(std::initializer_list< std::string > methods) { gen.methods(methods); return *this; } - + CoreHandlerMock & tid(std::string tid) { gen.tid(tid); return *this; @@ -62,20 +62,12 @@ class PamInfoMock { class MethodFactoryMock { public: - using MethodFactoryCreate_t = tl::expected< Method, PamAction >; - - template - MethodFactoryMock(Args && ... ) {} + using MethodFactoryCreate_t = tl::expected< Method, PamAction >; - MOCK_METHOD(MethodFactoryCreate_t, create, (const PamInfoMock&, std::string tid), (const)); -}; + template < typename... Args > + MethodFactoryMock(Args &&...) {} -class InitTestable : public Init< MethodFactoryMock, PamInfoMock > { - public: - InitTestable(PamInfoMock&pam, const rublon::Configuration & conf) : Init{pam, conf} {} - PamInfoMock & pam() { - return _pamInfo; - } + MOCK_METHOD(MethodFactoryCreate_t, create, (const PamInfoMock &, std::string tid), (const)); }; class RublonHttpInitTest : public testing::Test { @@ -84,15 +76,15 @@ class RublonHttpInitTest : public testing::Test { EXPECT_CALL(pam, ip()).WillOnce(Return("192.168.0.1")); EXPECT_CALL(pam, username()).WillOnce(Return("bwi")); } - - RublonHttpInitTest() : coreHandler{}, pam{}, sut{pam, conf}{ + + RublonHttpInitTest() : coreHandler{}, pam{}, sut{conf} { expectDefaultPamInfo(); } CoreHandlerMock coreHandler; PamInfoMock pam; - InitTestable sut; -// MethodFactoryMock &methodFactoryMock; + Init< MethodFactoryMock > sut; + // MethodFactoryMock &methodFactoryMock; }; using CoreReturn = tl::expected< Document, CoreHandlerError >; @@ -100,7 +92,7 @@ using CoreReturn = tl::expected< Document, CoreHandlerError >; TEST_F(RublonHttpInitTest, initializationSendsRequestOnGoodPath) { EXPECT_CALL(coreHandler, request("/api/transaction/init", _)) .WillOnce(Return(tl::unexpected{CoreHandlerError{CoreHandlerError::BadSigature}})); - sut.handle(coreHandler); + sut.handle(coreHandler, pam); } MATCHER_P(HoldsPamAction, action, "") { @@ -109,26 +101,27 @@ MATCHER_P(HoldsPamAction, action, "") { TEST_F(RublonHttpInitTest, rublon_Accept_pamLoginWhenThereIsNoConnection) { EXPECT_CALL(coreHandler, request(_, _)).WillOnce(Return(tl::unexpected{CoreHandlerError{CoreHandlerError::ConnectionError}})); - EXPECT_THAT(sut.handle(coreHandler), HoldsPamAction(PamAction::decline)); + EXPECT_THAT(sut.handle(coreHandler, pam), HoldsPamAction(PamAction::decline)); } TEST_F(RublonHttpInitTest, rublon_Decline_pamLoginWhenServerHasBadSignature) { EXPECT_CALL(coreHandler, request(_, _)).WillOnce(Return(tl::unexpected{CoreHandlerError{CoreHandlerError::BadSigature}})); - EXPECT_THAT(sut.handle(coreHandler), HoldsPamAction(PamAction::decline)); + EXPECT_THAT(sut.handle(coreHandler, pam), HoldsPamAction(PamAction::decline)); } TEST_F(RublonHttpInitTest, rublon_Decline_pamLoginWhenServerReturnsBrokenData) { EXPECT_CALL(coreHandler, request(_, _)).WillOnce(Return(tl::unexpected{CoreHandlerError{CoreHandlerError::BrokenData}})); - EXPECT_THAT(sut.handle(coreHandler), HoldsPamAction(PamAction::decline)); + EXPECT_THAT(sut.handle(coreHandler, pam), HoldsPamAction(PamAction::decline)); } TEST_F(RublonHttpInitTest, rublon_Decline_pamLoginWhenServerReturnsCoreException) { EXPECT_CALL(coreHandler, request(_, _)).WillOnce(Return(tl::unexpected{CoreHandlerError{CoreHandlerError::CoreException}})); - EXPECT_THAT(sut.handle(coreHandler), HoldsPamAction(PamAction::decline)); + EXPECT_THAT(sut.handle(coreHandler, pam), HoldsPamAction(PamAction::decline)); } TEST_F(RublonHttpInitTest, AllNeededInformationNeedsToBePassedToMethodFactory) { - EXPECT_CALL(coreHandler, request(_, _)).WillOnce(Return(coreHandler.statusPending().methods({"sms", "otp"}).tid("transaction ID").create())); -// EXPECT_CALL(methodFactoryMock, create_mocked(_,"transaction ID") ); - sut.handle(coreHandler); + EXPECT_CALL(coreHandler, request(_, _)) + .WillOnce(Return(coreHandler.statusPending().methods({"sms", "otp"}).tid("transaction ID").create())); + // EXPECT_CALL(methodFactoryMock, create_mocked(_,"transaction ID") ); + sut.handle(coreHandler, pam); }