rublon-ssh/PAM/ssh/tests/http_mock.hpp
rublon-bwi 9415174eba
Bwi/bugfix round2 (#9)
* Fix log file access, refactor configuration reading class

* Remove bypass option in favor of failmode

* fix loging, print enrolment info

* Add EMAIL method

* Add yubi authentication method

* Add support for verification message

* Add verification

* Made changes in Vagrant's files to run different OSs

* Switch off tests and packages demands to run PAM on Debian 11

* Add authentication totp

* Changes in utils

* Remove unnessesary interface

* Changed vagrant files and postinstal script for Ubuntu 20 and 22

* Moved adding PasswordAuth to vagrant file from posinst

* Added ubuntu 24.04

* Set version

* Poprawki UI

* WebSocket implementation 

* Add totp authentication method

* fixup changes in utils

* Remove unnessesary interface and simplify code

* Remove "default" message handler from WebSocket class

* Change display names of known authentication methods

* Cleanup code in 'main' file

* Add CheckApplication

* Remove unused function

* Changed vagrant files and postinstal script for Ubuntu 20 and 22

* Moved adding PasswordAuth to vagrant file from posinst

* Added ubuntu 24.04

* Set version to 2.0.2

* Proper handle for missing configuration

* Fixup use value of optional object

* Add more vCPU/RAM to vagrant VM's + fix translations

* Minor WS fixes, translations

* Proper handler for Werification error

* Make use of prompt parameter

* Add max number of prompts

* remove unused code, fir includes

* Add Waiting status

* Add check application status check

---------

Co-authored-by: Madzik <m.w@linux.pl>
2024-05-28 12:04:20 +02:00

271 lines
8.2 KiB
C++
Executable File

#pragma once
#include <gmock/gmock.h>
#include <set>
#include <string>
#include <rublon/configuration.hpp>
#include <rublon/curl.hpp>
#include "core_response_generator.hpp"
#include "rublon/sign.hpp"
namespace {
rublon::Configuration conf{rublon::Configuration{//
{"320BAB778C4D4262B54CD243CDEFFAFD"},
{"39e8d771d83a2ed3cc728811911c25"},
{"https://staging-core.rublon.net"},
1,
true,
true,
false,
rublon::FailMode::safe}};
rublon::Configuration confBypass{rublon::Configuration{//
{"320BAB778C4D4262B54CD243CDEFFAFD"},
{"39e8d771d83a2ed3cc728811911c25"},
{"https://staging-core.rublon.net"},
1,
true,
true,
false,
rublon::FailMode::safe}};
inline std::string randomTID() {
static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::string tmp_s;
const auto len = 32;
tmp_s.resize(len);
std::for_each(tmp_s.begin(), tmp_s.end(), [](auto & chr) { chr = alphanum[rand() % (sizeof(alphanum) - 1)]; });
return tmp_s;
}
std::string join_methods(std::set< std::string > _methods) {
std::string ret;
for(const auto & m : _methods)
ret += "\"" + m + "\",";
ret.pop_back();
return ret;
}
} // namespace
struct ResponseMockOptions {
bool generateBrokenSigneture{false};
bool generateBrokenBody{false};
bool generateBrokenCode{false};
std::string coreException{};
std::set< std::string > methods{};
std::optional<rublon::Error> error;
};
class InitCoreResponse {
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";
public:
static std::string generate(const ResponseMockOptions & options) {
if(options.generateBrokenBody) {
return result_broken_template;
}
std::array< char, 2048 > _buf;
auto tid = randomTID();
auto status = "pending";
std::string companyName{"rublon"};
std::string applicationName{"test_app"};
std::set< std::string > methods{"email", "totp", "qrcode", "push"};
if(options.methods.size())
methods = options.methods;
io::sprintf(_buf.data(), result_ok_template, tid, status, companyName, applicationName, join_methods(methods));
return _buf.data();
}
};
class MethodSelectCoreResponse {
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";
public:
static std::string generate(const ResponseMockOptions & options) {
if(options.generateBrokenBody) {
return result_broken_template;
}
std::array< char, 2048 > _buf;
auto tid = randomTID();
auto status = "pending";
std::string companyName{"rublon"};
std::string applicationName{"test_app"};
std::set< std::string > methods{"email", "totp", "qrcode", "push"};
io::sprintf(_buf.data(), result_ok_template, tid, status, companyName, applicationName, join_methods(methods));
return _buf.data();
}
};
class CodeVerificationResponse {
static constexpr const char * wrongPasscode =
R"json({"status":"OK","result":{"error":"Hmm, that's not the right code. Try again."}})json";
static constexpr const char * goodPasscode = R"json({"status":"OK","result":true})json";
public:
static std::string generate(const ResponseMockOptions & options) {
return options.generateBrokenCode ? wrongPasscode : goodPasscode;
}
};
class AuthenticationOkResponse {
static constexpr const char * authOk = R"json({"status":"OK","result":true})json";
public:
static std::string generate(const ResponseMockOptions &) {
return authOk;
}
};
template < typename Generator >
class ResponseBase {
public:
Generator & initMessage() {
_coreGenerator = InitCoreResponse{};
return static_cast< Generator & >(*this);
}
Generator & selectMethodMessage() {
_coreGenerator = MethodSelectCoreResponse{};
return static_cast< Generator & >(*this);
}
Generator & codeConfirmationMessage() {
_coreGenerator = CodeVerificationResponse{};
return static_cast< Generator & >(*this);
}
Generator & authenticationSuccessfullMessage() {
_coreGenerator = AuthenticationOkResponse{};
return static_cast< Generator & >(*this);
}
Generator & withBrokenBody() {
options.generateBrokenBody = true;
return static_cast< Generator & >(*this);
}
Generator & withCoreException() {
options.coreException = "some exception";
return static_cast< Generator & >(*this);
}
Generator & withBrokenSignature() {
options.generateBrokenSigneture = true;
return static_cast< Generator & >(*this);
}
Generator & withWrongCodeResponse() {
options.generateBrokenCode = true;
return static_cast< Generator & >(*this);
}
Generator & withTimeoutError() {
options.error = rublon::Error{rublon::ConnectionError{}};
return static_cast< Generator & >(*this);
}
Generator & withServiceUnavailableError() {
options.error = rublon::Error{rublon::ConnectionError{rublon::ConnectionError::HttpError, 405}};
return static_cast< Generator & >(*this);
}
template < typename... T >
Generator & withMethods(T... methods) {
(options.methods.insert(methods), ...);
return static_cast< Generator & >(*this);
}
Generator & withConnectionError() {
// options.error = rublon::Error{rublon::CoreHandlerError{rublon::CoreHandlerError::ConnectionError}};
return static_cast< Generator & >(*this);
}
Generator & withBasSignature() {
options.error = rublon::Error{rublon::CoreHandlerError{rublon::CoreHandlerError::BadSigature}};
return static_cast< Generator & >(*this);
}
operator auto() {
return not options.error.has_value() ? static_cast< Generator * >(this)->generate() : tl::unexpected{error()};
}
rublon::Error error() {
return rublon::Error{options.error.value()};
}
std::variant< InitCoreResponse, MethodSelectCoreResponse, CodeVerificationResponse, AuthenticationOkResponse > _coreGenerator;
ResponseMockOptions options;
};
class RawCoreResponse : public ResponseBase< RawCoreResponse > {
public:
tl::expected< rublon::Document, rublon::Error > generate() {
static rublon::RapidJSONPMRAlloc alloc;
auto jsonString = std::visit([&](const auto generator) { return generator.generate(options); }, _coreGenerator);
rublon::Document doc{&alloc};
doc.Parse(jsonString.c_str());
return doc;
}
};
class RawHttpResponse : public ResponseBase< RawHttpResponse > {
rublon::Response & response;
public:
RawHttpResponse(rublon::Response & r)
: ResponseBase< RawHttpResponse >(),
response{r} {
};
static auto signResponse(rublon::Response & res, ResponseMockOptions opts) {
const auto & sign =
opts.generateBrokenSigneture ? std::array< char, 65 >{} : rublon::signData(res.body, conf.secretKey.data());
res.headers["x-rublon-signature"] = sign.data();
}
tl::expected< std::reference_wrapper< rublon::Response >, rublon::Error > generate() {
response.body = std::visit([&](const auto generator) { return generator.generate(options); }, _coreGenerator);
signResponse(response, options);
return response;
}
};
class HttpHandlerMock {
public:
template < typename... Args >
HttpHandlerMock(const Args &...) {}
MOCK_METHOD(( tl::expected< std::reference_wrapper< rublon::Response >, rublon::Error > ),
request,
( std::string_view, const rublon::Request & ),
(const));
tl::expected< std::reference_wrapper< rublon::Response >, rublon::Error >
request(std::string_view uri, const rublon::Request & req, rublon::Response &) const {
return request(uri, req);
}
};