* Add phone call authentication method * Remove dynamic mem allocation from error handler * Add more error handling code * Move error handling to different file * Remove Socket IO dependency * cleanup in websocket code * Add rapidjson as cmake dependency * Added Dockerfiles as primary build system for packages * Changed policy in CMakeList to work with lower version of CMake * Fix opensuse builds * Link filesystem library in gcc 8.5 or older
270 lines
8.2 KiB
C++
270 lines
8.2 KiB
C++
#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);
|
|
}
|
|
};
|