init
This commit is contained in:
commit
dcab16178d
68
.clang-format
Executable file
68
.clang-format
Executable file
@ -0,0 +1,68 @@
|
|||||||
|
AccessModifierOffset: -2
|
||||||
|
AlignAfterOpenBracket: false
|
||||||
|
AlignConsecutiveAssignments: true
|
||||||
|
# AlignConsecutiveDeclarations: false
|
||||||
|
AlignEscapedNewlinesLeft: true
|
||||||
|
AlignOperands: false
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
# AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: true
|
||||||
|
AlwaysBreakTemplateDeclarations: true
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
BreakBeforeTernaryOperators: false
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
# BreakStringLiterals: false
|
||||||
|
ColumnLimit: 140
|
||||||
|
# CommentPragmas:
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
ConstructorInitializerIndentWidth: 1
|
||||||
|
ContinuationIndentWidth: 2
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DerivePointerAlignment : false
|
||||||
|
DisableFormat: false
|
||||||
|
# ExperimentalAutoDetectBinPacking: false
|
||||||
|
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||||
|
# IncludeCategories:
|
||||||
|
# IncludeIsMainRegex:
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentWidth: 4
|
||||||
|
# IndentWrappedFunctionNames:
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
Language: Cpp
|
||||||
|
# MacroBlockBegin:
|
||||||
|
# MacroBlockEnd:
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: Inner
|
||||||
|
# PenaltyBreakBeforeFirstCallParameter:
|
||||||
|
# PenaltyBreakComment:
|
||||||
|
# PenaltyBreakFirstLessLess:
|
||||||
|
# PenaltyBreakString:
|
||||||
|
# PenaltyExcessCharacter:
|
||||||
|
# PenaltyReturnTypeOnItsOwnLine:
|
||||||
|
PointerAlignment: Middle
|
||||||
|
# ReflowComments: false
|
||||||
|
SortIncludes: true
|
||||||
|
SpaceAfterCStyleCast: true
|
||||||
|
# SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeParens: Never
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: true
|
||||||
|
SpacesInCStyleCastParentheses: true
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
Standard: Cpp11
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
||||||
|
|
||||||
74
.gitignore
vendored
Normal file
74
.gitignore
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# This file is used to ignore files which are generated
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
*~
|
||||||
|
*.autosave
|
||||||
|
*.a
|
||||||
|
*.core
|
||||||
|
*.moc
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*_pch.h.cpp
|
||||||
|
*_resource.rc
|
||||||
|
*.qm
|
||||||
|
.#*
|
||||||
|
*.*#
|
||||||
|
core
|
||||||
|
!core/
|
||||||
|
tags
|
||||||
|
.DS_Store
|
||||||
|
.directory
|
||||||
|
*.debug
|
||||||
|
Makefile*
|
||||||
|
*.prl
|
||||||
|
*.app
|
||||||
|
moc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
qrc_*.cpp
|
||||||
|
Thumbs.db
|
||||||
|
*.res
|
||||||
|
*.rc
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
|
||||||
|
# qtcreator generated files
|
||||||
|
*.pro.user*
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
|
||||||
|
# xemacs temporary files
|
||||||
|
*.flc
|
||||||
|
|
||||||
|
# Vim temporary files
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
# Visual Studio generated files
|
||||||
|
*.ib_pdb_index
|
||||||
|
*.idb
|
||||||
|
*.ilk
|
||||||
|
*.pdb
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.vcproj
|
||||||
|
*vcproj.*.*.user
|
||||||
|
*.ncb
|
||||||
|
*.sdf
|
||||||
|
*.opensdf
|
||||||
|
*.vcxproj
|
||||||
|
*vcxproj.*
|
||||||
|
|
||||||
|
# MinGW generated files
|
||||||
|
*.Debug
|
||||||
|
*.Release
|
||||||
|
|
||||||
|
# Python byte code
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
# --------
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
|
||||||
10
CMakeLists.txt
Normal file
10
CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
project(rublon-ssh LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||||
|
|
||||||
|
add_subdirectory(PAM/ssh)
|
||||||
9
PAM/ssh/CMakeLists.txt
Normal file
9
PAM/ssh/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
add_subdirectory(extern/rapidjson)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
extern/rapidjson/include
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
||||||
|
|
||||||
|
add_subdirectory(tests)
|
||||||
1
PAM/ssh/extern/rapidjson
vendored
Submodule
1
PAM/ssh/extern/rapidjson
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 973dc9c06dcd3d035ebd039cfb9ea457721ec213
|
||||||
2444
PAM/ssh/extern/tl/expected.hpp
vendored
Normal file
2444
PAM/ssh/extern/tl/expected.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
PAM/ssh/src/CMakeLists.txt
Normal file
7
PAM/ssh/src/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(rublon-ssh -lcurl -lssl -lcrypto)
|
||||||
104
PAM/ssh/src/CoreHandler.hpp
Normal file
104
PAM/ssh/src/CoreHandler.hpp
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory_resource>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "configuration.hpp"
|
||||||
|
#include "curl.hpp"
|
||||||
|
#include "json.hpp"
|
||||||
|
#include "sign.hpp"
|
||||||
|
|
||||||
|
#include "../extern/tl/expected.hpp"
|
||||||
|
|
||||||
|
namespace rublon {
|
||||||
|
|
||||||
|
class CoreHandlerError {
|
||||||
|
public:
|
||||||
|
enum ErrorClass { BadSigature, CoreException, ConnectionError, BrokenData };
|
||||||
|
|
||||||
|
CoreHandlerError(ErrorClass e) : errorClass{e} {}
|
||||||
|
CoreHandlerError(ErrorClass e, std::string r) : errorClass{e}, reson{std::move(r)} {}
|
||||||
|
|
||||||
|
ErrorClass errorClass;
|
||||||
|
std::string reson;
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename Impl >
|
||||||
|
class CoreHandlerInterface {
|
||||||
|
public:
|
||||||
|
tl::expected< rublon::Document, CoreHandlerError > request(std::string_view path, const rublon::Document & body) const {
|
||||||
|
return static_cast< const Impl * >(this)->request(path, body);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename HttpHandler = CURL >
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
void signRequest(std::pmr::monotonic_buffer_resource & mr, Request & request) const {
|
||||||
|
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);
|
||||||
|
return xRubResp == sign.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
HttpHandler http{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
void debugLog(const char *, const char *) {}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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";
|
||||||
|
|
||||||
|
request.body = jsonStr.GetString();
|
||||||
|
|
||||||
|
signRequest(mr, request);
|
||||||
|
|
||||||
|
std::pmr::string uri{url + path.data(), &mr};
|
||||||
|
|
||||||
|
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};
|
||||||
|
}
|
||||||
|
|
||||||
|
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()}};
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace rublon
|
||||||
105
PAM/ssh/src/configuration.hpp
Normal file
105
PAM/ssh/src/configuration.hpp
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory_resource>
|
||||||
|
#include <optional>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
namespace rublon {
|
||||||
|
class ConfigurationFactory;
|
||||||
|
class Configuration {
|
||||||
|
public:
|
||||||
|
struct Parameters {
|
||||||
|
std::string systemToken;
|
||||||
|
std::string secretKey;
|
||||||
|
std::string apiServer;
|
||||||
|
int prompt;
|
||||||
|
bool enablePasswdEmail;
|
||||||
|
bool logging;
|
||||||
|
bool autopushPrompt;
|
||||||
|
};
|
||||||
|
|
||||||
|
Parameters parameters;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConfigurationFactory {
|
||||||
|
public:
|
||||||
|
ConfigurationFactory(){};
|
||||||
|
|
||||||
|
std::optional< Configuration > systemConfig() {
|
||||||
|
std::array< char, 8 * 1024 > configBuffer;
|
||||||
|
std::pmr::monotonic_buffer_resource mr{configBuffer.data(), configBuffer.size()};
|
||||||
|
|
||||||
|
using Params = Configuration::Parameters;
|
||||||
|
Params configValues;
|
||||||
|
|
||||||
|
/// TODO wypadałoby zmienić ścieżkę
|
||||||
|
std::ifstream file(std::filesystem::path{"/etc/rublon.config"});
|
||||||
|
|
||||||
|
std::pmr::string line{&mr};
|
||||||
|
line.reserve(100);
|
||||||
|
std::pmr::map< std::pmr::string, std::pmr::string > parameters{&mr};
|
||||||
|
|
||||||
|
while(std::getline(file, line)) {
|
||||||
|
std::pmr::string key{&mr};
|
||||||
|
std::pmr::string value{&mr};
|
||||||
|
|
||||||
|
if(!line.length())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(line[0] == '#' || line[0] == ';')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto posEqual = line.find('=');
|
||||||
|
key = line.substr(0, posEqual);
|
||||||
|
value = line.substr(posEqual + 1);
|
||||||
|
|
||||||
|
parameters.emplace(std::move(key), std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto saveStr = [&](auto member) { //
|
||||||
|
return [member](Params * params, std::string_view value) { params->*member = value; };
|
||||||
|
};
|
||||||
|
|
||||||
|
auto saveInt = [](auto member) { //
|
||||||
|
return [member](Params * params, std::string_view value) { params->*member = std::stoi(value.data()); };
|
||||||
|
};
|
||||||
|
|
||||||
|
auto saveBool = [](auto member) {
|
||||||
|
return [member](Params * params, std::string_view value) { params->*member = details::to_bool(value); };
|
||||||
|
};
|
||||||
|
|
||||||
|
std::tuple< const char *, std::function< void(Params *, const char *) >, const char * > checks[]{//
|
||||||
|
{"logging", saveBool(&Params::logging), "true"},
|
||||||
|
{"systemToken", saveStr(&Params::systemToken), nullptr},
|
||||||
|
{"secretKey", saveStr(&Params::secretKey), nullptr},
|
||||||
|
{"rublonApiServer", saveStr(&Params::apiServer), nullptr},
|
||||||
|
{"prompt", saveInt(&Params::prompt), "1"},
|
||||||
|
{"enablePasswdEmail", saveBool(&Params::enablePasswdEmail), "true"},
|
||||||
|
{"autopushPrompt", saveBool(&Params::autopushPrompt), "false"}};
|
||||||
|
|
||||||
|
for(const auto & [key, set_func, default_value] : checks) {
|
||||||
|
if(auto it = parameters.find(key); it != parameters.end()) {
|
||||||
|
set_func(&configValues, it->second.data());
|
||||||
|
} else {
|
||||||
|
if(default_value) {
|
||||||
|
/// TODO exit??
|
||||||
|
} else { // not required
|
||||||
|
set_func(&configValues, default_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Configuration{std::move(configValues)};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace rublon
|
||||||
84
PAM/ssh/src/curl.hpp
Normal file
84
PAM/ssh/src/curl.hpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
reinterpret_cast< std::string * >(userp)->append(static_cast< const char * >(contents), realsize);
|
||||||
|
return realsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Request{
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
std::string body;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Response{
|
||||||
|
std::map<std::string, std::string> headers;
|
||||||
|
std::string body;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class CURL {
|
||||||
|
std::unique_ptr< ::CURL, void (*)(::CURL *) > curl;
|
||||||
|
|
||||||
|
public:
|
||||||
|
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 {
|
||||||
|
std::string response_data;
|
||||||
|
response_data.reserve(1000);
|
||||||
|
|
||||||
|
|
||||||
|
Response response;
|
||||||
|
|
||||||
|
/// TODO this can be done on stack using pmr
|
||||||
|
auto curl_headers = std::unique_ptr< curl_slist, void (*)(curl_slist *) >(nullptr, curl_slist_free_all);
|
||||||
|
std::for_each(request.headers.begin(), request.headers.end(), [&](auto header) {
|
||||||
|
curl_headers.reset(curl_slist_append(curl_headers.release(),(header.first + ": " + header.second).c_str()));
|
||||||
|
});
|
||||||
|
|
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 1);
|
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_URL, uri.data());
|
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, curl_headers.get());
|
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_POST, 1);
|
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, request.body.data());
|
||||||
|
curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE, static_cast< u_int32_t >(request.body.size()));
|
||||||
|
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);
|
||||||
|
|
||||||
|
auto res = curl_easy_perform(curl.get());
|
||||||
|
if(res != CURLE_OK) {
|
||||||
|
// debugLog("No response from Rublon server (perform)", "");
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
long size;
|
||||||
|
curl_easy_getinfo(curl.get(), CURLINFO_HEADER_SIZE, &size);
|
||||||
|
|
||||||
|
///TODO ogarnąć alokację pamięci
|
||||||
|
response.headers = details::headers({response_data.data(), static_cast<std::size_t>(size)});
|
||||||
|
response.body = response_data.substr(size);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace rublon
|
||||||
0
PAM/ssh/src/init.hpp
Normal file
0
PAM/ssh/src/init.hpp
Normal file
88
PAM/ssh/src/json.hpp
Normal file
88
PAM/ssh/src/json.hpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "rapidjson/document.h"
|
||||||
|
#include "rapidjson/writer.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <memory_resource>
|
||||||
|
namespace rublon {
|
||||||
|
|
||||||
|
struct RapidJSONPMRAlloc {
|
||||||
|
std::pmr::memory_resource * upstream = std::pmr::get_default_resource();
|
||||||
|
|
||||||
|
static constexpr bool kNeedFree = true;
|
||||||
|
static constexpr auto objectOffset = alignof(std::max_align_t);
|
||||||
|
static constexpr auto memPadding = objectOffset * 2;
|
||||||
|
|
||||||
|
void * Malloc(size_t size) {
|
||||||
|
if(size != 0) {
|
||||||
|
const auto allocated_size = size + memPadding;
|
||||||
|
std::byte * newPtr = static_cast< std::byte * >(upstream->allocate(allocated_size));
|
||||||
|
auto * ptrToReturn = newPtr + memPadding;
|
||||||
|
// placement new a pointer to ourselves at the first memory location
|
||||||
|
new(newPtr)(RapidJSONPMRAlloc *)(this);
|
||||||
|
// placement new the size in the second location
|
||||||
|
new(newPtr + objectOffset)(std::size_t)(size);
|
||||||
|
return ptrToReturn;
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void freePtr(void * origPtr, size_t originalSize) {
|
||||||
|
if(origPtr == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
upstream->deallocate(static_cast< std::byte * >(origPtr) - memPadding, originalSize + memPadding);
|
||||||
|
}
|
||||||
|
|
||||||
|
void * Realloc(void * origPtr, size_t originalSize, size_t newSize) {
|
||||||
|
if(newSize == 0) {
|
||||||
|
freePtr(origPtr, originalSize);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(newSize <= originalSize) {
|
||||||
|
return origPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void * newPtr = Malloc(newSize);
|
||||||
|
std::memcpy(newPtr, origPtr, originalSize);
|
||||||
|
freePtr(origPtr, originalSize);
|
||||||
|
return newPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// and Free needs to be static, which causes this whole thing
|
||||||
|
// to fall apart. This means that we have to keep our own list of allocated memory
|
||||||
|
// with our own pointers back to ourselves and our own list of sizes
|
||||||
|
// so we can push all of this back to the upstream allocator
|
||||||
|
static void Free(void * ptr) {
|
||||||
|
if(ptr == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::byte * startOfData = static_cast< std::byte * >(ptr) - memPadding;
|
||||||
|
|
||||||
|
auto * ptrToAllocator = *reinterpret_cast< RapidJSONPMRAlloc ** >(startOfData);
|
||||||
|
auto origAllocatedSize = *reinterpret_cast< std::size_t * >(startOfData + objectOffset);
|
||||||
|
|
||||||
|
ptrToAllocator->freePtr(ptr, origAllocatedSize);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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 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
|
||||||
36
PAM/ssh/src/pam.cpp
Normal file
36
PAM/ssh/src/pam.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#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 <rapidjson/rapidjson.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "curl.hpp"
|
||||||
|
#include "rublon.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
PAM_EXTERN int pam_sm_setcred( pam_handle_t *pamh, int flags, int argc, const char **argv ) {
|
||||||
|
return PAM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) {
|
||||||
|
auto rublonConfig = rublon::ConfigurationFactory{}.systemConfig();
|
||||||
|
|
||||||
|
rublon::CoreHandler CH{rublonConfig.value()};
|
||||||
|
|
||||||
|
rublon::Init{rublonConfig.value()}.fire(CH);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
63
PAM/ssh/src/pam.hpp
Normal file
63
PAM/ssh/src/pam.hpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <security/pam_ext.h>
|
||||||
|
|
||||||
|
#include <security/pam_appl.h>
|
||||||
|
#include <security/pam_client.h>
|
||||||
|
#include <security/pam_ext.h>
|
||||||
|
#include <security/pam_misc.h>
|
||||||
|
#include <security/pam_modules.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <system_error>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
class PamAction {};
|
||||||
|
|
||||||
|
class PamAccept {};
|
||||||
|
class PamDecline {};
|
||||||
|
|
||||||
|
class PAMPrompt {
|
||||||
|
pam_handle_t * pamh;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template < typename... Ti >
|
||||||
|
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 {
|
||||||
|
char * responseBuffer = nullptr;
|
||||||
|
pam_prompt(pamh, PAM_TEXT_INFO, &responseBuffer, fmt, std::forward< Ti... >(ti...));
|
||||||
|
if(responseBuffer) {
|
||||||
|
auto ret = f(responseBuffer);
|
||||||
|
free(responseBuffer);
|
||||||
|
return std::optional{ret};
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
};
|
||||||
119
PAM/ssh/src/rublon.hpp
Normal file
119
PAM/ssh/src/rublon.hpp
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory_resource>
|
||||||
|
#include <optional>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "CoreHandler.hpp"
|
||||||
|
#include "json.hpp"
|
||||||
|
#include "pam.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
namespace rublon {
|
||||||
|
|
||||||
|
enum class PamAction { accept, decline };
|
||||||
|
|
||||||
|
template < typename Impl >
|
||||||
|
class AuthenticationStep {
|
||||||
|
public:
|
||||||
|
template < typename Hander_t >
|
||||||
|
auto fire(const CoreHandlerInterface< Hander_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 > > {
|
||||||
|
std::string tid;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Method() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename PamInfo_t = PAMInfo >
|
||||||
|
class Init : public AuthenticationStep< Init< PamInfo_t > > {
|
||||||
|
const char * apiPath = "/api/transaction/init";
|
||||||
|
const std::string & systemToken;
|
||||||
|
|
||||||
|
PamInfo_t pamInfo;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char * stepName = "Initialization";
|
||||||
|
|
||||||
|
Init(const rublon::Configuration & config) : systemToken{config.parameters.systemToken} {}
|
||||||
|
|
||||||
|
/// TODO add core handler interface
|
||||||
|
template < typename Hander_t >
|
||||||
|
std::variant< Method< PamInfo_t >, PamAction > handle(const CoreHandlerInterface< Hander_t > & handler) const {
|
||||||
|
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);
|
||||||
|
|
||||||
|
Value params{rapidjson::kObjectType};
|
||||||
|
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);
|
||||||
|
|
||||||
|
auto response = handler.request(apiPath, body);
|
||||||
|
|
||||||
|
if(response.has_value()) {
|
||||||
|
std::cout << response.value()["response"]["tid"].GetString();
|
||||||
|
|
||||||
|
return Method< PamInfo_t >{
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} 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};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConfirmCode : public AuthenticationStep< ConfirmCode > {
|
||||||
|
public:
|
||||||
|
ConfirmCode(const Configuration & /*config*/) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VerifySSH : public AuthenticationStep< VerifySSH > {
|
||||||
|
public:
|
||||||
|
VerifySSH(const Configuration & /*config*/) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Credentials : public AuthenticationStep< Credentials > {
|
||||||
|
public:
|
||||||
|
Credentials(const Configuration & /*config*/) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace rublon
|
||||||
25
PAM/ssh/src/sign.hpp
Normal file
25
PAM/ssh/src/sign.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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]);
|
||||||
|
return xRublon;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace rublon
|
||||||
329
PAM/ssh/src/span.hpp
Normal file
329
PAM/ssh/src/span.hpp
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
#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
|
||||||
107
PAM/ssh/src/utils.hpp
Normal file
107
PAM/ssh/src/utils.hpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstring>
|
||||||
|
#include <map>
|
||||||
|
#include <memory_resource>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <alloca.h>
|
||||||
|
|
||||||
|
namespace rublon {
|
||||||
|
|
||||||
|
template < typename T >
|
||||||
|
class NonOwningPtr {
|
||||||
|
T * object;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr NonOwningPtr(T * obj) : object{obj} {}
|
||||||
|
|
||||||
|
constexpr T * get() noexcept {
|
||||||
|
assert(object != nullptr);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
constexpr const T * get() const noexcept {
|
||||||
|
assert(object != nullptr);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
constexpr operator const T *() const noexcept {
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
constexpr operator T *() noexcept {
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
inline bool to_bool(std::string_view value) {
|
||||||
|
auto * buf = ( char * ) alloca(value.size());
|
||||||
|
buf[value.size()] = '\0';
|
||||||
|
auto asciitolower = [](char in) { return in - ((in <= 'Z' && in >= 'A') ? ('Z' - 'z') : 0); };
|
||||||
|
|
||||||
|
std::transform(value.cbegin(), value.cend(), buf, asciitolower);
|
||||||
|
return strcmp(buf, "true") == 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline std::string_view ltrim(std::string_view s) {
|
||||||
|
while(s.length() && std::isspace(*s.begin()))
|
||||||
|
s.remove_prefix(1);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string_view rtrim(std::string_view s) {
|
||||||
|
while(s.length() && std::isspace(*s.rbegin()))
|
||||||
|
s.remove_suffix(1);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string_view trim(std::string_view s) {
|
||||||
|
return ltrim(rtrim(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::map< std::string, std::string > headers(std::string_view data) {
|
||||||
|
std::map< std::string, std::string > headers{};
|
||||||
|
|
||||||
|
std::string tmp{};
|
||||||
|
std::istringstream resp{};
|
||||||
|
resp.rdbuf()->pubsetbuf(const_cast< char * >(data.data()), data.size());
|
||||||
|
|
||||||
|
while(std::getline(resp, tmp)) {
|
||||||
|
auto line = std::string_view(tmp);
|
||||||
|
auto index = tmp.find(':', 0);
|
||||||
|
if(index != std::string::npos) {
|
||||||
|
headers.insert({std::string{trim(line.substr(0, index))}, std::string{trim(line.substr(index + 1))}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace pmr {
|
||||||
|
inline std::pmr::map< std::pmr::string, std::pmr::string > headers(std::pmr::memory_resource * mr, std::string_view data) {
|
||||||
|
char _buf[1024];
|
||||||
|
std::pmr::monotonic_buffer_resource tmr{_buf, sizeof(_buf)};
|
||||||
|
std::pmr::map< std::pmr::string, std::pmr::string > headers{mr};
|
||||||
|
|
||||||
|
std::pmr::string tmp{&tmr};
|
||||||
|
std::istringstream resp{};
|
||||||
|
resp.rdbuf()->pubsetbuf(const_cast< char * >(data.data()), data.size());
|
||||||
|
|
||||||
|
while(std::getline(resp, tmp) && !(trim(tmp).empty())) {
|
||||||
|
auto line = std::string_view(tmp);
|
||||||
|
auto index = tmp.find(':', 0);
|
||||||
|
if(index != std::string::npos) {
|
||||||
|
headers.insert({std::pmr::string{trim(line.substr(0, index)), mr}, std::pmr::string{trim(line.substr(index + 1)), mr}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
} // namespace pmr
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
} // namespace rublon
|
||||||
24
PAM/ssh/tests/CMakeLists.txt
Normal file
24
PAM/ssh/tests/CMakeLists.txt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
enable_testing()
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
## Project-wide setup
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS NO)
|
||||||
|
|
||||||
|
# Externally provided libraries
|
||||||
|
FetchContent_Declare(googletest
|
||||||
|
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||||
|
GIT_TAG main)
|
||||||
|
|
||||||
|
FetchContent_Declare(googlebenchmark
|
||||||
|
GIT_REPOSITORY https://github.com/google/benchmark.git
|
||||||
|
GIT_TAG main)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(
|
||||||
|
googletest
|
||||||
|
googlebenchmark)
|
||||||
|
|
||||||
|
add_executable(rublon-tests utilsTests.cpp rublonTests.cpp core_handler_tests.cpp init_test.cpp)
|
||||||
|
target_link_libraries(rublon-tests GTest::gmock_main -lssl -lcrypto)
|
||||||
0
PAM/ssh/tests/core_handler_mock.hpp
Normal file
0
PAM/ssh/tests/core_handler_mock.hpp
Normal file
67
PAM/ssh/tests/core_handler_tests.cpp
Normal file
67
PAM/ssh/tests/core_handler_tests.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <numeric>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "../src/CoreHandler.hpp"
|
||||||
|
#include "http_mock.hpp"
|
||||||
|
|
||||||
|
class CoreHandlerTestable : public rublon::CoreHandler< HttpHandlerMock > {
|
||||||
|
public:
|
||||||
|
CoreHandlerTestable() : rublon::CoreHandler< HttpHandlerMock >{conf} {}
|
||||||
|
auto & _http() {
|
||||||
|
return http;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CoreHandlerTests : public testing::Test {
|
||||||
|
public:
|
||||||
|
CoreHandlerTests() : http{sut._http()} {
|
||||||
|
doc.SetObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreHandlerTestable sut;
|
||||||
|
HttpHandlerMock & http;
|
||||||
|
|
||||||
|
rublon::Document doc;
|
||||||
|
};
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
MATCHER(HasValue, "") {
|
||||||
|
return arg.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const rublon::CoreHandlerError & lhs, const rublon::CoreHandlerError & rhs) {
|
||||||
|
return lhs.errorClass == rhs.errorClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
MATCHER_P(Unexpected, error, "") {
|
||||||
|
return arg.error() == error;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CoreHandlerTests, coreShouldSetConnectionErrorOnBrokenConnection) {
|
||||||
|
EXPECT_CALL(http, request(_, _)).WillOnce(Return(std::optional< rublon::Response >{}));
|
||||||
|
EXPECT_THAT(sut.request("", doc), //
|
||||||
|
AllOf(Not(HasValue()), Unexpected(rublon::CoreHandlerError::ConnectionError)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CoreHandlerTests, coreShouldCheckSignatureAndReturnBadSignatureBeforeAnythingElse) {
|
||||||
|
EXPECT_CALL(http, request(_, _)).WillOnce(Return((rublon::Response) http.brokenSignature().brokenBody()));
|
||||||
|
EXPECT_THAT(sut.request("", doc), //
|
||||||
|
AllOf(Not(HasValue()), Unexpected(rublon::CoreHandlerError::BadSigature)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CoreHandlerTests, coreShouldSetBrokenDataWhenResultIsNotAvailable) {
|
||||||
|
EXPECT_CALL(http, request(_, _)).WillOnce(Return(( rublon::Response ) http.brokenBody()));
|
||||||
|
EXPECT_THAT(sut.request("", doc), //
|
||||||
|
AllOf(Not(HasValue()), Unexpected(rublon::CoreHandlerError::BrokenData)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CoreHandlerTests, coreSignatureIsBeingChecked) {
|
||||||
|
EXPECT_CALL(http, request(Eq(conf.parameters.apiServer + "/path"), _)).WillOnce(Return(( rublon::Response ) http.statusPending()));
|
||||||
|
auto val = sut.request("/path", doc);
|
||||||
|
|
||||||
|
EXPECT_TRUE(val.value().IsObject());
|
||||||
|
}
|
||||||
86
PAM/ssh/tests/core_response_generator.hpp
Normal file
86
PAM/ssh/tests/core_response_generator.hpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace io {
|
||||||
|
template < class X >
|
||||||
|
using is_string = typename std::enable_if_t< std::is_same_v< X, std::string > >;
|
||||||
|
|
||||||
|
template < class X >
|
||||||
|
using is_not_string = typename std::enable_if_t< !std::is_same_v< X, std::string > >;
|
||||||
|
|
||||||
|
template < class T, class = is_not_string< T > >
|
||||||
|
constexpr T forward_or_transform(T t) {
|
||||||
|
return std::forward< T >(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
template < class T, class = is_string< T > >
|
||||||
|
constexpr const char * forward_or_transform(T t) {
|
||||||
|
return t.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
template < class... Ti >
|
||||||
|
int sprintf(char * _s, const std::string & format, Ti... t) {
|
||||||
|
return std::sprintf(_s, format.c_str(), forward_or_transform(t)...);
|
||||||
|
}
|
||||||
|
} // namespace io
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::string gen_random(const int len) {
|
||||||
|
static const char alphanum[] =
|
||||||
|
"0123456789"
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
std::string tmp_s;
|
||||||
|
tmp_s.resize(len);
|
||||||
|
|
||||||
|
std::for_each(tmp_s.begin(), tmp_s.end(), [](auto & chr) { chr = alphanum[rand() % (sizeof(alphanum) - 1)]; });
|
||||||
|
|
||||||
|
return tmp_s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr const char * result_ok_template =
|
||||||
|
R"json({"status":"OK","result":{"tid":"%s","status":"%s","companyName":"%s","applicationName":"%s","methods":[%s]}})json";
|
||||||
|
|
||||||
|
static constexpr const char * result_broken_template = R"json({"some":"random","json":"notrublon"})json";
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class CoreResponseGenerator {
|
||||||
|
public:
|
||||||
|
std::string generateBody() {
|
||||||
|
std::array< char, 2048 > _buf;
|
||||||
|
io::sprintf(_buf.data(),
|
||||||
|
generateBrokenData ? result_broken_template : result_ok_template,
|
||||||
|
tid,
|
||||||
|
status,
|
||||||
|
companyName,
|
||||||
|
applicationName,
|
||||||
|
print_methods());
|
||||||
|
return _buf.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string print_methods() {
|
||||||
|
std::string ret;
|
||||||
|
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};
|
||||||
|
};
|
||||||
60
PAM/ssh/tests/http_mock.hpp
Normal file
60
PAM/ssh/tests/http_mock.hpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "../src/curl.hpp"
|
||||||
|
#include "../src/configuration.hpp"
|
||||||
|
|
||||||
|
#include "core_response_generator.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
rublon::Configuration conf{rublon::Configuration::Parameters{//
|
||||||
|
"320BAB778C4D4262B54CD243CDEFFAFD",
|
||||||
|
"39e8d771d83a2ed3cc728811911c25",
|
||||||
|
"https://staging-core.rublon.net",
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false}};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class HttpHandlerMock {
|
||||||
|
auto signResponse(rublon::Response & res) {
|
||||||
|
const auto & sign =
|
||||||
|
skipSignatureGeneration ? std::array< char, 64 >{} : rublon::signData(res.body, conf.parameters.secretKey.c_str());
|
||||||
|
res.headers["x-rublon-signature"] = sign.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
MOCK_METHOD(std::optional< rublon::Response >, request, ( std::string_view, const rublon::Request & ), (const));
|
||||||
|
|
||||||
|
HttpHandlerMock & statusPending() {
|
||||||
|
gen.status = "pending";
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpHandlerMock & brokenBody() {
|
||||||
|
gen.generateBrokenData = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpHandlerMock & brokenSignature() {
|
||||||
|
skipSignatureGeneration = true;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator rublon::Response() {
|
||||||
|
rublon::Response res;
|
||||||
|
res.body = gen.generateBody();
|
||||||
|
|
||||||
|
signResponse(res);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool skipSignatureGeneration;
|
||||||
|
CoreResponseGenerator gen;
|
||||||
|
};
|
||||||
58
PAM/ssh/tests/init_test.cpp
Normal file
58
PAM/ssh/tests/init_test.cpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "../src/rublon.hpp"
|
||||||
|
|
||||||
|
#include "core_response_generator.hpp"
|
||||||
|
|
||||||
|
using namespace rublon;
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
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 RublonHttpInitTest : public testing::Test {
|
||||||
|
public:
|
||||||
|
CoreHandlerMock coreHandler;
|
||||||
|
Init<> sut{conf};
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RublonHttpInitTest, returnMethods){
|
||||||
|
|
||||||
|
}
|
||||||
7
PAM/ssh/tests/rublonTests.cpp
Normal file
7
PAM/ssh/tests/rublonTests.cpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
|
#include "../src/rublon.hpp"
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
90
PAM/ssh/tests/utilsTests.cpp
Normal file
90
PAM/ssh/tests/utilsTests.cpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include <cassert>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <memory_resource>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "../src/utils.hpp"
|
||||||
|
|
||||||
|
using namespace rublon;
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
TEST(Utils, toBoolReturnsTrueWhenStringIsPassed) {
|
||||||
|
EXPECT_TRUE(details::to_bool("TrUe"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Utils, toBoolReturnsFalseWhenStringIsPassed) {
|
||||||
|
EXPECT_FALSE(details::to_bool("False"));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string response = R"http(HTTP/2 200
|
||||||
|
date: Thu, 22 Jun 2023 13:24:58 GMT
|
||||||
|
content-type: application/json
|
||||||
|
server: nginx
|
||||||
|
cache-control: no-cache, private
|
||||||
|
x-rublon-signature: 1a01558bedaa2dd92ff659fb8ee3c65a89163d63e312fcb9b6f60463cce864d7
|
||||||
|
x-ratelimit-limit: 300
|
||||||
|
x-ratelimit-remaining: 299
|
||||||
|
|
||||||
|
{"status":"OK","result":{"tid":"2039132542F6465691BF8C41D7CC38C5","status":"pending","companyName":"rublon","applicationName":"Bartoszek_SSH","methods":["email","totp","qrcode","push"]}})http";
|
||||||
|
|
||||||
|
static inline std::string_view ltrim(std::string_view s) {
|
||||||
|
while(std::isspace(*s.begin()))
|
||||||
|
s.remove_prefix(1);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string_view rtrim(std::string_view s) {
|
||||||
|
while(std::isspace(*s.rbegin()))
|
||||||
|
s.remove_suffix(1);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string_view trim(std::string_view s) {
|
||||||
|
return ltrim(rtrim(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pmr::map< std::pmr::string, std::pmr::string > headers(std::pmr::memory_resource * mr, std::string_view data) {
|
||||||
|
std::pmr::map< std::pmr::string, std::pmr::string > headers{mr};
|
||||||
|
|
||||||
|
std::pmr::string tmp{mr};
|
||||||
|
tmp.reserve(256);
|
||||||
|
std::istringstream resp{};
|
||||||
|
resp.rdbuf()->pubsetbuf(const_cast< char * >(data.data()), data.size());
|
||||||
|
|
||||||
|
while(std::getline(resp, tmp) && !(trim(tmp).empty())) {
|
||||||
|
auto line = std::string_view(tmp);
|
||||||
|
auto index = tmp.find(':', 0);
|
||||||
|
if(index != std::string::npos) {
|
||||||
|
headers.insert({std::pmr::string{trim(line.substr(0, index)), mr}, std::pmr::string{trim(line.substr(index + 1)), mr}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::map< std::string, std::string > headers(std::string_view data) {
|
||||||
|
std::map< std::string, std::string > headers{};
|
||||||
|
|
||||||
|
std::string tmp{};
|
||||||
|
std::istringstream resp{};
|
||||||
|
resp.rdbuf()->pubsetbuf(const_cast< char * >(data.data()), data.size());
|
||||||
|
|
||||||
|
while(std::getline(resp, tmp) && !(trim(tmp).empty())) {
|
||||||
|
auto line = std::string_view(tmp);
|
||||||
|
auto index = tmp.find(':', 0);
|
||||||
|
if(index != std::string::npos) {
|
||||||
|
headers.insert({std::string{trim(line.substr(0, index))}, std::string{trim(line.substr(index + 1))}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Utils, responseParser) {
|
||||||
|
auto sut = headers(response);
|
||||||
|
EXPECT_THAT(sut,
|
||||||
|
AllOf(Contains(Pair("date", "Thu, 22 Jun 2023 13:24:58 GMT")),
|
||||||
|
Contains(Pair("x-rublon-signature", "1a01558bedaa2dd92ff659fb8ee3c65a89163d63e312fcb9b6f60463cce864d7")),
|
||||||
|
Contains(Pair("x-ratelimit-remaining", "299"))));
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user