Remove template PAM from class defs

This commit is contained in:
Bartosz Wieczorek 2023-08-07 11:40:38 +02:00
parent e9b65c294e
commit d035219202
17 changed files with 201 additions and 526 deletions

View File

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

View File

@ -1,5 +1,6 @@
#pragma once
#include "rublon/pam.hpp"
#include "rublon/pam_action.hpp"
#include <rublon/core_handler_interface.hpp>
#include <rublon/utils.hpp>
@ -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;
}
};

View File

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

View File

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

View File

@ -8,41 +8,35 @@
#include <rublon/method/method_factory.hpp>
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

View File

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

View File

@ -6,54 +6,40 @@
#include <rublon/pam.hpp>
#include <rublon/pam_action.hpp>
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};
}
};

View File

@ -1,8 +1,45 @@
#pragma once
#include <tl/expected.hpp>
#include <rublon/authentication_step_interface.hpp>
#include <rublon/pam.hpp>
#include <rublon/pam_action.hpp>
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

View File

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

View File

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

View File

@ -1,5 +1,10 @@
#pragma once
namespace rublon{
namespace rublon {
enum class PamAction { accept, decline };
}
class AuthenticationStatus {};
} // namespace rublon

View File

@ -12,9 +12,6 @@
#include <sstream>
#include <string>
#include <iostream>
#include <variant>
#include <rublon/method/method_factory.hpp>

View File

@ -1,329 +0,0 @@
#pragma once
#include <array> // for std::array, etc.
#include <cassert> // for assert
#include <cstddef> // for std::size_t, etc.
#include <iterator> // for std::reverse_iterator, etc.
#include <type_traits> // 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<std::size_t>::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 <array>
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

View File

@ -6,11 +6,10 @@
#include <rapidjson/rapidjson.h>
#include <rublon/init.hpp>
#include <rublon/pam.hpp>
#include <rublon/rublon.hpp>
#include <rublon/utils.hpp>
#include <rublon/pam.hpp>
#include <rublon/init.hpp>
#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<rublon::LinuxPam> & 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;
}

View File

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

View File

@ -0,0 +1,4 @@
#include <gtest/gtest.h>
#include <rublon/authentication_step_interface.hpp>

View File

@ -27,12 +27,12 @@ class CoreHandlerMock : public CoreHandlerInterface< CoreHandlerMock > {
gen.generateBrokenData = true;
return *this;
}
CoreHandlerMock & methods(std::initializer_list<std::string> 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<PamInfoMock>, PamAction >;
template<typename ... Args>
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);
}