snapshot
This commit is contained in:
parent
dcab16178d
commit
9d6790c3cf
@ -1,7 +1,9 @@
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "-fpic -static-libstdc++ -fvisibility=hidden -ffunction-sections -fdata-sections -fwhole-program ")
|
||||
|
||||
add_library(
|
||||
rublon-ssh SHARED pam.cpp pam.hpp rublon.hpp curl.hpp span.hpp sign.hpp CoreHandler.hpp configuration.hpp
|
||||
)
|
||||
|
||||
#set(CMAKE_SHARED_LINKER_FLAGS "-fpic -static-libstdc++ -fvisibility=hidden -ffunction-sections -fdata-sections -fwhole-program")
|
||||
|
||||
target_link_options(rublon-ssh PUBLIC -fpic -shared)
|
||||
|
||||
target_link_libraries(rublon-ssh -lcurl -lssl -lcrypto)
|
||||
|
||||
@ -27,6 +27,7 @@ template < typename Impl >
|
||||
class CoreHandlerInterface {
|
||||
public:
|
||||
tl::expected< rublon::Document, CoreHandlerError > request(std::string_view path, const rublon::Document & body) const {
|
||||
rublon::debugLog("[TMP] request",__PRETTY_FUNCTION__);
|
||||
return static_cast< const Impl * >(this)->request(path, body);
|
||||
}
|
||||
};
|
||||
@ -36,17 +37,19 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
|
||||
std::string secretKey;
|
||||
std::string url;
|
||||
|
||||
std::pmr::string xRublonSignature(std::pmr::memory_resource * mr, std::string_view body) const {
|
||||
return signData(body, secretKey.c_str()).data();
|
||||
std::pmr::string xRublonSignature(std::pmr::memory_resource & mr, std::string_view body) const {
|
||||
return {signData(body, secretKey.c_str()).data(), &mr};
|
||||
}
|
||||
|
||||
void signRequest(std::pmr::monotonic_buffer_resource & mr, Request & request) const {
|
||||
request.headers["X-Rublon-Signature"] = xRublonSignature(&mr, request.body);
|
||||
rublon::debugLog("[TMP]",__PRETTY_FUNCTION__);
|
||||
request.headers["X-Rublon-Signature"] = xRublonSignature(mr, request.body);
|
||||
}
|
||||
|
||||
bool responseSigned(const Response & response) const {
|
||||
auto xRubResp = response.headers.at("x-rublon-signature");
|
||||
auto sign = signData(response.body, secretKey);
|
||||
rublon::debugLog("[TMP]",__PRETTY_FUNCTION__);
|
||||
const auto & xRubResp = response.headers.at("x-rublon-signature");
|
||||
const auto & sign = signData(response.body, secretKey);
|
||||
return xRubResp == sign.data();
|
||||
}
|
||||
|
||||
@ -59,10 +62,12 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
|
||||
CoreHandler(const rublon::Configuration & config) : secretKey{config.parameters.secretKey}, url{config.parameters.apiServer} {}
|
||||
|
||||
tl::expected< rublon::Document, CoreHandlerError > request(std::string_view path, const rublon::Document & body) const {
|
||||
rublon::debugLog("[TMP] request",__PRETTY_FUNCTION__);
|
||||
|
||||
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};
|
||||
|
||||
@ -73,31 +78,33 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
|
||||
request.headers["Accept"] = "application/json";
|
||||
|
||||
request.body = jsonStr.GetString();
|
||||
|
||||
|
||||
rublon::debugLog("[TMP] request", "sign");
|
||||
signRequest(mr, request);
|
||||
|
||||
std::pmr::string uri{url + path.data(), &mr};
|
||||
|
||||
auto response = http.request(uri, request);
|
||||
// std::pmr::string uri{url + path.data(), &mr};
|
||||
|
||||
if(not response.has_value()) {
|
||||
// rublon::debugLog("[TMP] send", request.body.c_str());
|
||||
// auto response = http.request(uri, request);
|
||||
|
||||
// if(not response.has_value()) {
|
||||
return tl::unexpected{CoreHandlerError::ConnectionError};
|
||||
}
|
||||
if(not responseSigned(*response)) {
|
||||
return tl::unexpected{CoreHandlerError::BadSigature};
|
||||
}
|
||||
// }
|
||||
// if(not responseSigned(*response)) {
|
||||
// return tl::unexpected{CoreHandlerError::BadSigature};
|
||||
// }
|
||||
|
||||
rublon::Document resp{&alloc};
|
||||
resp.Parse(response->body.c_str());
|
||||
// rublon::Document resp{&alloc};
|
||||
// resp.Parse(response->body.c_str());
|
||||
|
||||
if(resp.HasParseError() or not resp.HasMember("result")) {
|
||||
return tl::unexpected{CoreHandlerError::BrokenData};
|
||||
}
|
||||
if(resp["result"].HasMember("exception")) {
|
||||
return tl::unexpected{CoreHandlerError{CoreHandlerError::CoreException, resp["result"]["exception"].GetString()}};
|
||||
}
|
||||
// if(resp.HasParseError() or not resp.HasMember("result")) {
|
||||
// return tl::unexpected{CoreHandlerError::BrokenData};
|
||||
// }
|
||||
// if(resp["result"].HasMember("exception")) {
|
||||
// return tl::unexpected{CoreHandlerError{CoreHandlerError::CoreException, resp["result"]["exception"].GetString()}};
|
||||
// }
|
||||
|
||||
return resp;
|
||||
// return resp;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -11,12 +11,8 @@
|
||||
|
||||
#include <memory_resource>
|
||||
|
||||
#include "span.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
/// TODO rename file to rublon/Core.hpp
|
||||
/// TODO Create rublon utils
|
||||
|
||||
namespace rublon {
|
||||
static size_t WriteMemoryCallback(void * contents, size_t size, size_t nmemb, void * userp) {
|
||||
size_t realsize = size * nmemb;
|
||||
@ -42,6 +38,8 @@ class CURL {
|
||||
CURL() : curl{std::unique_ptr< ::CURL, void (*)(::CURL *) >(curl_easy_init(), curl_easy_cleanup)} {}
|
||||
|
||||
std::optional< Response > request(std::string_view uri, const Request &request) const {
|
||||
rublon::debugLog("[TMP] request",__PRETTY_FUNCTION__);
|
||||
|
||||
std::string response_data;
|
||||
response_data.reserve(1000);
|
||||
|
||||
@ -63,7 +61,9 @@ class CURL {
|
||||
curl_easy_setopt(curl.get(), CURLOPT_HEADER, 1);
|
||||
curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
|
||||
curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response_data);
|
||||
|
||||
|
||||
rublon::debugLog("[TMP] calling curl",__PRETTY_FUNCTION__);
|
||||
|
||||
auto res = curl_easy_perform(curl.get());
|
||||
if(res != CURLE_OK) {
|
||||
// debugLog("No response from Rublon server (perform)", "");
|
||||
|
||||
@ -74,15 +74,4 @@ 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 value = GenericValue< UTF8<>, RapidJSONPMRAlloc >;
|
||||
|
||||
// RapidJSONPMRAlloc alloc{&mr};
|
||||
// GenericDocument< UTF8<>, RapidJSONPMRAlloc > d(&alloc);
|
||||
// d.SetObject();
|
||||
|
||||
// GenericDocument< UTF8<>, RapidJSONPMRAlloc >::AllocatorType & allocator = d.GetAllocator();
|
||||
|
||||
// d.AddMember("systemToken", "DUPA", allocator);
|
||||
// d.AddMember("username", "$USER", allocator);
|
||||
// d.AddMember("userEmail", "$USER_EMAIL", allocator);
|
||||
} // namespace rublon
|
||||
|
||||
@ -1,36 +1,56 @@
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/pam_modules.h>
|
||||
#include <security/pam_misc.h>
|
||||
#include <security/pam_client.h>
|
||||
#include <security/pam_ext.h>
|
||||
#include <security/pam_misc.h>
|
||||
#include <security/pam_modules.h>
|
||||
|
||||
#include <rapidjson/rapidjson.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "curl.hpp"
|
||||
#include "rublon.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
PAM_EXTERN int pam_sm_setcred( pam_handle_t *pamh, int flags, int argc, const char **argv ) {
|
||||
namespace {
|
||||
|
||||
template < typename T >
|
||||
using Expected = tl::expected< T, rublon::PamAction >;
|
||||
|
||||
Expected< rublon::Method > chooseMethod() {}
|
||||
|
||||
Expected< rublon::Confirm > confirm() {}
|
||||
|
||||
} // namespace
|
||||
|
||||
PAM_EXTERN int pam_sm_setcred(pam_handle_t * pamh, int flags, int argc, const char ** argv) {
|
||||
rublon::debugLog("[TMP]start", __PRETTY_FUNCTION__);
|
||||
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) {
|
||||
PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc, const char ** argv) {
|
||||
rublon::debugLog("[TMP]start", __PRETTY_FUNCTION__);
|
||||
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char ** argv) {
|
||||
rublon::debugLog("[TMP]start", __PRETTY_FUNCTION__);
|
||||
auto rublonConfig = rublon::ConfigurationFactory{}.systemConfig();
|
||||
|
||||
|
||||
rublon::debugLog("[TMP]create core handler", "");
|
||||
rublon::CoreHandler CH{rublonConfig.value()};
|
||||
|
||||
rublon::Init{rublonConfig.value()}.fire(CH);
|
||||
|
||||
|
||||
rublon::debugLog("[TMP]start", "");
|
||||
auto action = rublon::Init{pamh, rublonConfig.value()}
|
||||
.fire(CH) //
|
||||
.and_then([](const auto & method) -> Expected< int > {
|
||||
rublon::debugLog("[TMP]start", "init lambda");
|
||||
return tl::unexpected{rublon::PamAction::accept};
|
||||
});
|
||||
|
||||
// .and_then([](const auto & value) { return tl::expected< rublon::Configuration, rublon::PamAction >{}; }) //
|
||||
// .and_then([](const auto & conf) { return tl::expected< rublon::Configuration, rublon::PamAction >{}; });
|
||||
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
PAM_EXTERN int pam_sm_authenticate( pam_handle_t *pamh, int flags,int argc, const char **argv ) {
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
int main(){
|
||||
pam_sm_acct_mgmt(nullptr, 1, 0, nullptr);
|
||||
}
|
||||
|
||||
@ -18,24 +18,39 @@
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
class PamAction {};
|
||||
|
||||
class PamAccept {};
|
||||
class PamDecline {};
|
||||
|
||||
class PAMPrompt {
|
||||
class LinuxPam {
|
||||
pam_handle_t * pamh;
|
||||
|
||||
public:
|
||||
LinuxPam(pam_handle_t * handler) : pamh{handler} {}
|
||||
|
||||
rublon::NonOwningPtr< const char > ip() const {
|
||||
const void * ip = NULL;
|
||||
pam_get_item(pamh, PAM_RHOST, &ip);
|
||||
if(ip == NULL)
|
||||
ip = "";
|
||||
rublon::debugLog(__PRETTY_FUNCTION__, (const char*)ip);
|
||||
return (const char*)ip;
|
||||
}
|
||||
|
||||
rublon::NonOwningPtr< const char > username() const {
|
||||
const char * user = NULL;
|
||||
pam_get_user(pamh, &user, nullptr);
|
||||
if(user == NULL)
|
||||
user = "";
|
||||
rublon::debugLog(__PRETTY_FUNCTION__, user);
|
||||
return user;
|
||||
}
|
||||
|
||||
template < typename... Ti >
|
||||
void print(const char * fmt, Ti... ti) const noexcept {
|
||||
// pam_prompt(pamh, PAM_TEXT_INFO, nullptr, fmt, std::forward<Ti...>(ti...));
|
||||
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 {
|
||||
char * responseBuffer = nullptr;
|
||||
pam_prompt(pamh, PAM_TEXT_INFO, &responseBuffer, fmt, std::forward< Ti... >(ti...));
|
||||
pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &responseBuffer, fmt, std::forward< Ti... >(ti...));
|
||||
if(responseBuffer) {
|
||||
auto ret = f(responseBuffer);
|
||||
free(responseBuffer);
|
||||
@ -44,20 +59,3 @@ class PAMPrompt {
|
||||
return std::optional< std::result_of_t< Fun(char *) > >();
|
||||
}
|
||||
};
|
||||
|
||||
class LinuxPam {
|
||||
pam_handle_t * pamh;
|
||||
|
||||
public:
|
||||
rublon::NonOwningPtr< const char > ip() const {
|
||||
const char * ip = NULL;
|
||||
pam_get_item(pamh, PAM_RHOST, ( const void ** ) &ip);
|
||||
if(ip == NULL)
|
||||
ip = "";
|
||||
return ip;
|
||||
}
|
||||
|
||||
rublon::NonOwningPtr< const char > username() const {
|
||||
// pam_get_user
|
||||
}
|
||||
};
|
||||
|
||||
@ -26,85 +26,147 @@ enum class PamAction { accept, decline };
|
||||
template < typename Impl >
|
||||
class AuthenticationStep {
|
||||
public:
|
||||
template < typename Hander_t >
|
||||
auto fire(const CoreHandlerInterface< Hander_t > & coreHandler) {
|
||||
template < typename Handler_t >
|
||||
auto fire(const CoreHandlerInterface< Handler_t > & coreHandler) {
|
||||
// log step
|
||||
// debugLog("Starting %s step", static_cast<Impl*>(this)->stepName );
|
||||
return static_cast< Impl * >(this)->handle(coreHandler);
|
||||
}
|
||||
};
|
||||
|
||||
template < typename PamInfo_t = PAMInfo >
|
||||
class Method : public AuthenticationStep< Method< PamInfo_t > > {
|
||||
class Confirm : public AuthenticationStep< Confirm > {
|
||||
public:
|
||||
Confirm(const Configuration & /*config*/) {}
|
||||
};
|
||||
|
||||
class MethodOTP {};
|
||||
|
||||
class MethodSMS {};
|
||||
|
||||
|
||||
class Method : public AuthenticationStep< Method > {
|
||||
std::string tid;
|
||||
|
||||
public:
|
||||
Method() {}
|
||||
|
||||
template < typename Handler_t >
|
||||
tl::expected< Confirm, PamAction > fire(const CoreHandlerInterface< Handler_t > & coreHandler) {}
|
||||
|
||||
private:
|
||||
std::variant< MethodOTP, MethodSMS > _impl;
|
||||
};
|
||||
|
||||
template < typename PamInfo_t = PAMInfo >
|
||||
class Init : public AuthenticationStep< Init< PamInfo_t > > {
|
||||
const char * apiPath = "/api/transaction/init";
|
||||
const std::string & systemToken;
|
||||
template < typename Pam_t = LinuxPam >
|
||||
class MethodFactory {
|
||||
const Pam_t & pam;
|
||||
|
||||
PamInfo_t pamInfo;
|
||||
public:
|
||||
MethodFactory(const Pam_t & pam) : pam{pam} {}
|
||||
|
||||
template < typename Array_t >
|
||||
tl::expected< Method, PamAction > create(const Array_t & methods) const {
|
||||
rublon::debugLog("[TMP] ",__PRETTY_FUNCTION__);
|
||||
std::pmr::map< int, std::pmr::string > methods_id;
|
||||
pam.print("%s", "");
|
||||
int i;
|
||||
for(const auto & method : methods) {
|
||||
if(method == "email") {
|
||||
pam.print("%d: Email Link", i + 1);
|
||||
methods_id[++i] = "email";
|
||||
}
|
||||
if(method == "qrcode") {
|
||||
pam.print("%d: QR Code", i + 1);
|
||||
methods_id[++i] = "qrcode";
|
||||
}
|
||||
if(method == "totp") {
|
||||
pam.print("%d: Mobile TOTP", i + 1);
|
||||
methods_id[++i] = "totp";
|
||||
}
|
||||
if(method == "push") {
|
||||
pam.print("%d: Mobile Push", i + 1);
|
||||
methods_id[++i] = "push";
|
||||
}
|
||||
if(method == "sms") {
|
||||
pam.print("%d: SMS code", i + 1);
|
||||
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");
|
||||
|
||||
return tl::unexpected{PamAction::accept};
|
||||
}
|
||||
};
|
||||
|
||||
template < template < typename > class MethodFactory_t = MethodFactory, typename PamInfo_t = LinuxPam >
|
||||
class Init : public AuthenticationStep< Init< MethodFactory_t, PamInfo_t > > {
|
||||
const char * apiPath = "/api/transaction/init";
|
||||
const std::string & _systemToken;
|
||||
|
||||
protected:
|
||||
PamInfo_t _pamInfo;
|
||||
MethodFactory_t< PamInfo_t > _methodFactory;
|
||||
|
||||
public:
|
||||
const char * stepName = "Initialization";
|
||||
|
||||
Init(const rublon::Configuration & config) : systemToken{config.parameters.systemToken} {}
|
||||
Init(pam_handle_t * pamHandler, const rublon::Configuration & config)
|
||||
: _systemToken{config.parameters.systemToken}, _pamInfo{pamHandler}, _methodFactory{_pamInfo} {
|
||||
rublon::debugLog("[TMP] Init ctor",__PRETTY_FUNCTION__);
|
||||
}
|
||||
|
||||
/// TODO add core handler interface
|
||||
template < typename Hander_t >
|
||||
std::variant< Method< PamInfo_t >, PamAction > handle(const CoreHandlerInterface< Hander_t > & handler) const {
|
||||
tl::expected< Method, PamAction > handle(const CoreHandlerInterface< Hander_t > & coreHandler) const {
|
||||
rublon::debugLog("[TMP] init handle",__PRETTY_FUNCTION__);
|
||||
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("username", Value{pamInfo.username().get(), alloc}, alloc);
|
||||
body.AddMember("userEmail", "bwi@rublon.com", alloc);
|
||||
body.AddMember("systemToken", Value{_systemToken.c_str(), alloc}, alloc);
|
||||
body.AddMember("username", Value{_pamInfo.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{_pamInfo.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
|
||||
|
||||
body.AddMember("params", std::move(params), alloc);
|
||||
rublon::debugLog("[TMP] calling coreHandler",__PRETTY_FUNCTION__);
|
||||
auto httpResponse = coreHandler.request(apiPath, body);
|
||||
|
||||
auto response = handler.request(apiPath, body);
|
||||
// if(httpResponse.has_value()) {
|
||||
// rublon::debugLog("[TMP] has response, processing",__PRETTY_FUNCTION__);
|
||||
// const auto & rublonResponse = httpResponse.value()["response"];
|
||||
|
||||
if(response.has_value()) {
|
||||
std::cout << response.value()["response"]["tid"].GetString();
|
||||
// std::string tid = rublonResponse["tid"].GetString();
|
||||
|
||||
return Method< PamInfo_t >{
|
||||
// return _methodFactory.create(rublonResponse["methods"].GetArray());
|
||||
// } else {
|
||||
// // mostly connectio errors
|
||||
// switch(httpResponse.error().errorClass) {
|
||||
// case CoreHandlerError::ErrorClass::BadSigature:
|
||||
// return tl::unexpected{PamAction::decline};
|
||||
// case CoreHandlerError::ErrorClass::CoreException: /// TODO exception handling
|
||||
// return tl::unexpected{PamAction::decline}; /// TODO accept?
|
||||
// case CoreHandlerError::ErrorClass::ConnectionError:
|
||||
// return tl::unexpected{PamAction::decline}; /// TODO decline?
|
||||
// case CoreHandlerError::ErrorClass::BrokenData:
|
||||
// return tl::unexpected{PamAction::decline};
|
||||
// }
|
||||
// }
|
||||
|
||||
};
|
||||
|
||||
} else {
|
||||
// mostly connectio errors
|
||||
switch(response.error().errorClass) {
|
||||
case CoreHandlerError::ErrorClass::BadSigature:
|
||||
return PamAction::decline; /// TODO accept?
|
||||
case CoreHandlerError::ErrorClass::CoreException:
|
||||
return PamAction::decline; /// TODO accept?
|
||||
case CoreHandlerError::ErrorClass::ConnectionError:
|
||||
return PamAction::decline; /// TODO accept?
|
||||
case CoreHandlerError::ErrorClass::BrokenData:
|
||||
return PamAction::decline;
|
||||
}
|
||||
}
|
||||
|
||||
return {PamAction::decline};
|
||||
return tl::unexpected{PamAction::decline};
|
||||
}
|
||||
};
|
||||
|
||||
class ConfirmCode : public AuthenticationStep< ConfirmCode > {
|
||||
public:
|
||||
ConfirmCode(const Configuration & /*config*/) {}
|
||||
};
|
||||
|
||||
|
||||
class VerifySSH : public AuthenticationStep< VerifySSH > {
|
||||
public:
|
||||
|
||||
@ -1,21 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/pam_modules.h>
|
||||
#include <string_view>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace rublon {
|
||||
|
||||
inline std::array< char, 64 > signData(std::string_view data, std::string_view secretKey) {
|
||||
std::array< char, 64 > xRublon;
|
||||
unsigned char md[EVP_MAX_MD_SIZE] = {0};
|
||||
unsigned int md_len;
|
||||
|
||||
HMAC(EVP_sha256(), secretKey.data(), secretKey.size(), ( unsigned const char * ) data.data(), data.size(), md, &md_len);
|
||||
|
||||
int i;
|
||||
for(i = 0; i < 32; i++)
|
||||
sprintf(&xRublon[i * 2], "%02x", ( unsigned int ) md[i]);
|
||||
|
||||
@ -9,9 +9,26 @@
|
||||
#include <string_view>
|
||||
|
||||
#include <alloca.h>
|
||||
#include <cassert>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
#include <security/pam_modules.h>
|
||||
#include <syslog.h>
|
||||
|
||||
namespace rublon {
|
||||
|
||||
inline void debugLog(const char * message1, const char * message2) {
|
||||
auto file_name = "/tmp/rublon.log";
|
||||
auto fp = fopen(file_name, "a");
|
||||
fprintf(fp, "[%s] %s %s\n", "cmake version", message1, message2);
|
||||
fflush(fp);
|
||||
fclose(fp);
|
||||
|
||||
// openlog("pam_rublon", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
|
||||
// syslog(LOG_NOTICE, "[%s] %s %s", "cmake version", message1, message2);
|
||||
// closelog();
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
class NonOwningPtr {
|
||||
T * object;
|
||||
@ -33,6 +50,12 @@ class NonOwningPtr {
|
||||
constexpr operator T *() noexcept {
|
||||
return get();
|
||||
}
|
||||
constexpr T * operator->() {
|
||||
return get();
|
||||
}
|
||||
constexpr const T * operator->() const {
|
||||
return get();
|
||||
}
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
@ -9,50 +9,107 @@ using namespace rublon;
|
||||
using namespace testing;
|
||||
|
||||
namespace {
|
||||
Configuration conf;
|
||||
Configuration conf;
|
||||
}
|
||||
|
||||
|
||||
class CoreHandlerMock : public CoreHandlerInterface< CoreHandlerMock > {
|
||||
public:
|
||||
CoreHandlerMock() {}
|
||||
|
||||
MOCK_METHOD(( tl::expected< Document, CoreHandlerError > ), request, ( std::string_view, const Document & ), (const));
|
||||
|
||||
|
||||
CoreHandlerMock & statusPending() {
|
||||
gen.status = "pending";
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
CoreHandlerMock & brokenBody() {
|
||||
gen.generateBrokenData = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
operator tl::expected< Document, CoreHandlerError >() {
|
||||
auto body = gen.generateBody();
|
||||
|
||||
|
||||
rublon::Document doc;
|
||||
doc.Parse(body.c_str());
|
||||
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
|
||||
CoreResponseGenerator gen;
|
||||
};
|
||||
|
||||
class PamInfoMock {
|
||||
public:
|
||||
PamInfoMock(pam_handle_t *) {}
|
||||
MOCK_METHOD(rublon::NonOwningPtr< const char >, ip, (), (const));
|
||||
MOCK_METHOD(rublon::NonOwningPtr< const char >, username, (), (const));
|
||||
};
|
||||
|
||||
template < typename Pam >
|
||||
class MethodFactoryMock {
|
||||
public:
|
||||
MethodFactoryMock(const Pam &) {}
|
||||
template < typename Array_t >
|
||||
tl::expected< Method, PamAction > create(const Array_t & methods) const {}
|
||||
};
|
||||
|
||||
class InitTestable : public Init< MethodFactoryMock, PamInfoMock > {
|
||||
public:
|
||||
InitTestable(const rublon::Configuration & conf) : Init{nullptr, conf} {}
|
||||
PamInfoMock & pam() {
|
||||
return _pamInfo;
|
||||
}
|
||||
};
|
||||
|
||||
class RublonHttpInitTest : public testing::Test {
|
||||
public:
|
||||
void expectDefaultPamInfo() {
|
||||
EXPECT_CALL(pam, ip()).WillOnce(Return("192.168.0.1"));
|
||||
EXPECT_CALL(pam, username()).WillOnce(Return("bwi"));
|
||||
}
|
||||
|
||||
RublonHttpInitTest() : coreHandler{}, sut{conf}, pam{sut.pam()} {}
|
||||
CoreHandlerMock coreHandler;
|
||||
Init<> sut{conf};
|
||||
InitTestable sut{conf};
|
||||
PamInfoMock & pam;
|
||||
};
|
||||
|
||||
using CoreReturn = tl::expected< Document, CoreHandlerError >;
|
||||
|
||||
TEST_F(RublonHttpInitTest, initializationSendsRequestOnGoodPath) {
|
||||
EXPECT_CALL(coreHandler, request("/api/transaction/init", _)).WillOnce(Return(tl::unexpected{CoreHandlerError{CoreHandlerError::BadSigature}}));
|
||||
expectDefaultPamInfo();
|
||||
EXPECT_CALL(coreHandler, request("/api/transaction/init", _))
|
||||
.WillOnce(Return(tl::unexpected{CoreHandlerError{CoreHandlerError::BadSigature}}));
|
||||
sut.handle(coreHandler);
|
||||
}
|
||||
|
||||
TEST_F(RublonHttpInitTest, returnMethods){
|
||||
|
||||
MATCHER_P(HoldsPamAction, action, "") {
|
||||
return not arg.has_value() && arg.error() == 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));
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user