From e9f612753bba3805dfd471a1817bdf4c84d482ef Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Mon, 24 Jul 2023 13:25:48 +0200 Subject: [PATCH] fix build on debian --- PAM/ssh/CMakeLists.txt | 10 ++-- PAM/ssh/include/rublon/init.hpp | 6 +-- .../include/rublon/method/method_factory.hpp | 40 +++++++++------- PAM/ssh/include/rublon/utils.hpp | 2 +- PAM/ssh/tests/core_response_generator.hpp | 35 +++++++++----- PAM/ssh/tests/http_mock.hpp | 10 ++-- PAM/ssh/tests/init_test.cpp | 47 ++++++++++++++++--- 7 files changed, 100 insertions(+), 50 deletions(-) diff --git a/PAM/ssh/CMakeLists.txt b/PAM/ssh/CMakeLists.txt index 549425f..55bce18 100644 --- a/PAM/ssh/CMakeLists.txt +++ b/PAM/ssh/CMakeLists.txt @@ -3,6 +3,9 @@ include_directories(extern/rapidjson/include) add_library(rublon-ssh INTERFACE +) + +add_library(rublon-ssh_ide INTERFACE ./include/rublon/authentication_step_interface.hpp ./include/rublon/configuration.hpp ./include/rublon/core_handler.hpp @@ -18,16 +21,13 @@ add_library(rublon-ssh ./include/rublon/rublon.hpp ./include/rublon/sign.hpp ./include/rublon/span.hpp - ./include/rublon/utils.hpp -) - + ./include/rublon/utils.hpp) target_include_directories(rublon-ssh - PUBLIC INTERFACE + INTERFACE extern ${CMAKE_CURRENT_LIST_DIR}/include ) - add_subdirectory(lib) add_subdirectory(tests) diff --git a/PAM/ssh/include/rublon/init.hpp b/PAM/ssh/include/rublon/init.hpp index 72d22b8..5e4ae9f 100644 --- a/PAM/ssh/include/rublon/init.hpp +++ b/PAM/ssh/include/rublon/init.hpp @@ -48,11 +48,9 @@ class Init : public AuthenticationStep< Init< MethodFactory_t, PamInfo_t > > { if(httpResponse.has_value()) { log(LogLevel::Info, "[TMP] has response, processing", __PRETTY_FUNCTION__); - const auto & rublonResponse = httpResponse.value()["response"]; - + const auto & rublonResponse = httpResponse.value()["result"]; std::string tid = rublonResponse["tid"].GetString(); - - return _methodFactory.create(rublonResponse["methods"].GetArray()); + return _methodFactory.create(rublonResponse["methods"].GetArray(), std::move(tid)); } else { // mostly connectio errors switch(httpResponse.error().errorClass) { diff --git a/PAM/ssh/include/rublon/method/method_factory.hpp b/PAM/ssh/include/rublon/method/method_factory.hpp index a147d7f..7c127f0 100644 --- a/PAM/ssh/include/rublon/method/method_factory.hpp +++ b/PAM/ssh/include/rublon/method/method_factory.hpp @@ -5,25 +5,25 @@ #include #include -#include #include +#include #include #include -namespace rublon{ +namespace rublon { class Method { std::string tid; - + public: Method() {} - + template < typename Handler_t > - tl::expected< int , PamAction > fire(const CoreHandlerInterface< Handler_t > & coreHandler) { + tl::expected< int, PamAction > fire(const CoreHandlerInterface< Handler_t > & coreHandler) { return std::visit([&](const auto & method) { return method.fire(coreHandler); }, _impl); } - + private: std::variant< method::OTP, method::SMS > _impl; }; @@ -31,16 +31,17 @@ class Method { template < typename Pam_t = LinuxPam > class MethodFactory { const Pam_t & pam; - + public: MethodFactory(const Pam_t & pam) : pam{pam} {} - + template < typename Array_t > - tl::expected< Method, PamAction > create(const Array_t & methods) const { + tl::expected< Method, PamAction > create(const Array_t & methods, const std::string & tid) const { std::pmr::map< int, std::pmr::string > methods_id; pam.print("%s", ""); int i; for(const auto & method : methods) { + rublon::log(LogLevel::Debug, "method %s found at pos %d", method.GetString(), i); if(method == "email") { pam.print("%d: Email Link", i + 1); methods_id[++i] = "email"; @@ -62,15 +63,20 @@ class MethodFactory { methods_id[++i] = "sms"; } } - - auto methodid = pam.scan([](char * userinput) { return std::stoi(userinput); }, "\nSelect method [1-%d]: ", methods.Size()); - - pam.print("you selected: %s", methods_id.count(methodid.value_or(0)) ? methods_id.at(methodid.value_or(0)).c_str() : "unknown option"); - - - + + auto methodid = pam.scan( + [](char * userinput) { + rublon::log(LogLevel::Debug, "User input: %s", userinput); + return std::stoi(userinput); + }, + "\nSelect method [1-%d]: ", + methods.Size()); + + pam.print( + "you selected: %s", methods_id.count(methodid.value_or(0)) ? methods_id.at(methodid.value_or(0)).c_str() : "unknown option"); + return tl::unexpected{PamAction::accept}; } }; -} +} // namespace rublon diff --git a/PAM/ssh/include/rublon/utils.hpp b/PAM/ssh/include/rublon/utils.hpp index 18c68b0..9b2e660 100644 --- a/PAM/ssh/include/rublon/utils.hpp +++ b/PAM/ssh/include/rublon/utils.hpp @@ -30,7 +30,7 @@ inline auto dateStr() { constexpr const char* LogLevelNames[] {"Debug", "Info", "Warning", "Error"}; -static LogLevel g_level = Debug; +constexpr LogLevel g_level = Debug; namespace details{ static void doLog(LogLevel level, const char * line) noexcept { diff --git a/PAM/ssh/tests/core_response_generator.hpp b/PAM/ssh/tests/core_response_generator.hpp index a8f7d01..85243b8 100644 --- a/PAM/ssh/tests/core_response_generator.hpp +++ b/PAM/ssh/tests/core_response_generator.hpp @@ -55,32 +55,45 @@ class CoreResponseGenerator { std::array< char, 2048 > _buf; io::sprintf(_buf.data(), generateBrokenData ? result_broken_template : result_ok_template, - tid, + _tid, status, companyName, applicationName, print_methods()); return _buf.data(); } + + CoreResponseGenerator & methods(std::initializer_list< std::string > newMethods) { + _methods.clear(); + std::copy(newMethods.begin(), newMethods.end(), std::inserter(_methods, _methods.begin())); + return *this; + } + CoreResponseGenerator & tid(std::string tid) { + _tid = tid; + return *this; + } + + std::string _tid{generateTid()}; + std::string status; + std::string companyName{"rublon"}; + std::string applicationName{"test_app"}; + std::set< std::string > _methods{"email", "totp", "qrcode", "push"}; + + bool skipSignatureGeneration{false}; + bool generateBrokenData{false}; + private: std::string print_methods() { std::string ret; - for(const auto & m : methods) + for(const auto & m : _methods) ret += "\"" + m + "\","; ret.pop_back(); return ret; } - + static std::string generateTid() { return gen_random(32); } - - std::string tid{generateTid()}; - std::string status; - std::string companyName{"rublon"}; - std::string applicationName{"test_app"}; - std::set< std::string > methods{"email", "totp", "qrcode", "push"}; - bool skipSignatureGeneration{false}; - bool generateBrokenData{false}; + }; diff --git a/PAM/ssh/tests/http_mock.hpp b/PAM/ssh/tests/http_mock.hpp index f35941f..b7341e6 100644 --- a/PAM/ssh/tests/http_mock.hpp +++ b/PAM/ssh/tests/http_mock.hpp @@ -9,6 +9,7 @@ #include #include "core_response_generator.hpp" +#include "rublon/sign.hpp" namespace { rublon::Configuration conf{rublon::Configuration::Parameters{// @@ -21,7 +22,7 @@ rublon::Configuration conf{rublon::Configuration::Parameters{// false}}; } // namespace -class HttpHandlerMock { +class HttpHandlerMock : public CoreResponseGenerator{ auto signResponse(rublon::Response & res) { const auto & sign = skipSignatureGeneration ? std::array< char, 64 >{} : rublon::signData(res.body, conf.parameters.secretKey.c_str()); @@ -32,12 +33,12 @@ class HttpHandlerMock { MOCK_METHOD(std::optional< rublon::Response >, request, ( std::string_view, const rublon::Request & ), (const)); HttpHandlerMock & statusPending() { - gen.status = "pending"; + status = "pending"; return *this; } HttpHandlerMock & brokenBody() { - gen.generateBrokenData = true; + generateBrokenData = true; return *this; } @@ -48,7 +49,7 @@ class HttpHandlerMock { operator rublon::Response() { rublon::Response res; - res.body = gen.generateBody(); + res.body = generateBody(); signResponse(res); @@ -56,5 +57,4 @@ class HttpHandlerMock { } bool skipSignatureGeneration; - CoreResponseGenerator gen; }; diff --git a/PAM/ssh/tests/init_test.cpp b/PAM/ssh/tests/init_test.cpp index 886ef60..008a174 100644 --- a/PAM/ssh/tests/init_test.cpp +++ b/PAM/ssh/tests/init_test.cpp @@ -27,6 +27,16 @@ class CoreHandlerMock : public CoreHandlerInterface< CoreHandlerMock > { gen.generateBrokenData = true; return *this; } + + CoreHandlerMock & methods(std::initializer_list methods) { + gen.methods(methods); + return *this; + } + + CoreHandlerMock & tid(std::string tid) { + gen.tid(tid); + return *this; + } operator tl::expected< Document, CoreHandlerError >() { auto body = gen.generateBody(); @@ -37,6 +47,10 @@ class CoreHandlerMock : public CoreHandlerInterface< CoreHandlerMock > { return doc; } + auto create() { + return static_cast< tl::expected< Document, CoreHandlerError > >(*this); + } + CoreResponseGenerator gen; }; @@ -50,9 +64,21 @@ class PamInfoMock { template < typename Pam > class MethodFactoryMock { public: + using MethodFactoryCreate_t = tl::expected< Method, PamAction >; + MethodFactoryMock(const Pam &) {} + + MOCK_METHOD(MethodFactoryCreate_t, create_mocked, (std::vector< std::string > methods, std::string tid), (const)); + template < typename Array_t > - tl::expected< Method, PamAction > create(const Array_t & methods) const {} + MethodFactoryCreate_t create(const Array_t & methods, const std::string & tid) const { + std::vector< std::string > _methods; + std::transform(std::begin(methods), std::end(methods), std::back_inserter(_methods), [](const auto & el) { + return std::string{el.GetString()}; + }); + + return create_mocked(_methods, tid); + } }; class InitTestable : public Init< MethodFactoryMock, PamInfoMock > { @@ -61,6 +87,9 @@ class InitTestable : public Init< MethodFactoryMock, PamInfoMock > { PamInfoMock & pam() { return _pamInfo; } + MethodFactoryMock & methodFactory(){ + return _methodFactory; + } }; class RublonHttpInitTest : public testing::Test { @@ -69,17 +98,20 @@ class RublonHttpInitTest : public testing::Test { EXPECT_CALL(pam, ip()).WillOnce(Return("192.168.0.1")); EXPECT_CALL(pam, username()).WillOnce(Return("bwi")); } + + RublonHttpInitTest() : coreHandler{}, sut{conf}, pam{sut.pam()}, methodFactoryMock{sut.methodFactory()} { + expectDefaultPamInfo(); + } - RublonHttpInitTest() : coreHandler{}, sut{conf}, pam{sut.pam()} {} CoreHandlerMock coreHandler; InitTestable sut{conf}; PamInfoMock & pam; + MethodFactoryMock &methodFactoryMock; }; using CoreReturn = tl::expected< Document, CoreHandlerError >; TEST_F(RublonHttpInitTest, initializationSendsRequestOnGoodPath) { - expectDefaultPamInfo(); EXPECT_CALL(coreHandler, request("/api/transaction/init", _)) .WillOnce(Return(tl::unexpected{CoreHandlerError{CoreHandlerError::BadSigature}})); sut.handle(coreHandler); @@ -90,26 +122,27 @@ MATCHER_P(HoldsPamAction, action, "") { } TEST_F(RublonHttpInitTest, rublon_Accept_pamLoginWhenThereIsNoConnection) { - expectDefaultPamInfo(); EXPECT_CALL(coreHandler, request(_, _)).WillOnce(Return(tl::unexpected{CoreHandlerError{CoreHandlerError::ConnectionError}})); EXPECT_THAT(sut.handle(coreHandler), HoldsPamAction(PamAction::decline)); } TEST_F(RublonHttpInitTest, rublon_Decline_pamLoginWhenServerHasBadSignature) { - expectDefaultPamInfo(); EXPECT_CALL(coreHandler, request(_, _)).WillOnce(Return(tl::unexpected{CoreHandlerError{CoreHandlerError::BadSigature}})); EXPECT_THAT(sut.handle(coreHandler), HoldsPamAction(PamAction::decline)); } TEST_F(RublonHttpInitTest, rublon_Decline_pamLoginWhenServerReturnsBrokenData) { - expectDefaultPamInfo(); EXPECT_CALL(coreHandler, request(_, _)).WillOnce(Return(tl::unexpected{CoreHandlerError{CoreHandlerError::BrokenData}})); EXPECT_THAT(sut.handle(coreHandler), HoldsPamAction(PamAction::decline)); } TEST_F(RublonHttpInitTest, rublon_Decline_pamLoginWhenServerReturnsCoreException) { - expectDefaultPamInfo(); EXPECT_CALL(coreHandler, request(_, _)).WillOnce(Return(tl::unexpected{CoreHandlerError{CoreHandlerError::CoreException}})); EXPECT_THAT(sut.handle(coreHandler), 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); +}