143 lines
5.6 KiB
C++
143 lines
5.6 KiB
C++
#pragma once
|
|
|
|
#include <memory_resource>
|
|
#include <string>
|
|
|
|
#include "configuration.hpp"
|
|
#include "curl.hpp"
|
|
#include "json.hpp"
|
|
#include "sign.hpp"
|
|
|
|
#include <tl/expected.hpp>
|
|
|
|
#include <rublon/core_handler_interface.hpp>
|
|
|
|
namespace rublon {
|
|
|
|
template < typename HttpHandler = CURL >
|
|
class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > {
|
|
std::string secretKey;
|
|
std::string url;
|
|
bool bypass;
|
|
mutable RapidJSONPMRAlloc alloc{std::pmr::get_default_resource()};
|
|
|
|
void signRequest(Request & request) const {
|
|
request.headers["X-Rublon-Signature"] = std::pmr::string{signData(request.body, secretKey).data(), request.headers.get_allocator()};
|
|
}
|
|
|
|
bool responseSigned(const Response & response) const {
|
|
const auto & xRubResp = response.headers.at("x-rublon-signature");
|
|
const auto & sign = signData(response.body, secretKey);
|
|
const bool signatureMatch = xRubResp == sign.data();
|
|
if(not signatureMatch)
|
|
log(LogLevel::Error, "Signature mismatch %s != %s ", xRubResp.c_str(), sign.data());
|
|
return signatureMatch;
|
|
}
|
|
|
|
protected:
|
|
HttpHandler http{};
|
|
|
|
public:
|
|
CoreHandler(const Configuration & config)
|
|
: secretKey{config.parameters.secretKey}, url{config.parameters.apiServer}, bypass{config.parameters.bypass}, http{} {}
|
|
|
|
tl::expected< std::reference_wrapper< const Response >, Error > validateSignature(const Response & response) const {
|
|
if(not responseSigned(response)) {
|
|
log(LogLevel::Error, "CoreHandlerError::BadSigature");
|
|
return tl::unexpected{CoreHandlerError{CoreHandlerError::BadSigature}};
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
tl::expected<Document, Error > validateResponse(RapidJSONPMRAlloc &alloc, const Response & response) const {
|
|
log(LogLevel::Debug, "TRACE %s::%s:%d", "CoreHandler", "validateResponse", __LINE__);
|
|
Document resp{&alloc};
|
|
resp.Parse(response.body.c_str());
|
|
|
|
if(resp.HasParseError() or not resp.HasMember("result")) {
|
|
log(LogLevel::Error, "rublon Core responded with broken data");
|
|
return tl::unexpected{CoreHandlerError{CoreHandlerError::BrokenData}};
|
|
}
|
|
|
|
if(resp.HasMember("result") and resp["result"].IsObject() and resp["result"].HasMember("exception")) {
|
|
const auto & exception = resp["result"]["exception"].GetString();
|
|
log(LogLevel::Error, "rublon Core exception %s", exception);
|
|
return handleCoreException(exception);
|
|
}
|
|
|
|
return resp;
|
|
}
|
|
|
|
tl::unexpected< Error > handleCoreException(std::string_view exceptionString) const {
|
|
log(LogLevel::Debug, "TRACE %s::%s:%d", "CoreHandler", "handleCoreException", __LINE__);
|
|
if(exceptionString == "UserBypassedException" or exceptionString == "UserNotFoundException") {
|
|
log(LogLevel::Debug, "TRACE %s::%s:%d", "CoreHandler", "handleCoreException", __LINE__);
|
|
return tl::unexpected{Error{PamBaypass{}}};
|
|
} else {
|
|
log(LogLevel::Debug, "TRACE %s::%s:%d", "CoreHandler", "handleCoreException", __LINE__);
|
|
return tl::unexpected{
|
|
Error{CoreHandlerError{CoreHandlerError::CoreException, std::string{exceptionString.data(), exceptionString.size()}}}};
|
|
}
|
|
}
|
|
|
|
tl::unexpected< Error > handleHttpError() const {
|
|
log(LogLevel::Debug, "TRACE %s::%s:%d", "CoreHandler", "handleHttpError", __LINE__);
|
|
if(bypass) {
|
|
log(LogLevel::Warning, "User login bypass");
|
|
return tl::unexpected{Error{PamBaypass{}}};
|
|
} else {
|
|
log(LogLevel::Warning, "User login deny due to HTTP error");
|
|
return tl::unexpected{Error{PamDeny{}}};
|
|
}
|
|
}
|
|
|
|
tl::expected< Document, Error > handleError(const Error & error) const {
|
|
log(LogLevel::Debug, "TRACE %s::%s:%d", "CoreHandler", "handleError", __LINE__);
|
|
if(error.is< HttpError >() and error.hasClass(HttpError::Error)) {
|
|
return handleHttpError();
|
|
}
|
|
|
|
return tl::unexpected{Error{error}};
|
|
}
|
|
|
|
template < typename T >
|
|
static void stringifyTo(const Document & body, T & to) {
|
|
memory::Monotonic_2k_HeapResource tmpResource;
|
|
RapidJSONPMRAlloc alloc{&tmpResource};
|
|
StringBuffer jsonStr{&alloc};
|
|
Writer writer{jsonStr, &alloc};
|
|
body.Accept(writer);
|
|
to = jsonStr.GetString();
|
|
}
|
|
|
|
tl::expected< Document, Error > request(RapidJSONPMRAlloc &mr, std::string_view path, const Document & body) const {
|
|
log(LogLevel::Debug, "TRACE %s::%s:%d", "CoreHandler", "validateResponse", __LINE__);
|
|
memory::StrictMonotonic_4k_HeapResource memoryResource;
|
|
|
|
const auto validateSignature = [this](const auto & arg) { return this->validateSignature(arg); };
|
|
const auto validateResponse = [&](const auto & arg) { return this->validateResponse(mr, arg); };
|
|
const auto handleError = [this](const auto & error) { return this->handleError(error); };
|
|
const auto pmrs = [&](auto txt) { return std::pmr::string{txt, &memoryResource}; };
|
|
|
|
Request request{&memoryResource};
|
|
Response response{&memoryResource};
|
|
|
|
stringifyTo(body, request.body);
|
|
|
|
request.headers["Content-Type"] = pmrs("application/json");
|
|
request.headers["Accept"] = pmrs("application/json");
|
|
signRequest(request);
|
|
|
|
std::pmr::string uri{url + path.data(), &memoryResource};
|
|
|
|
return http
|
|
.request(uri, request, response) //
|
|
.and_then(validateSignature)
|
|
.and_then(validateResponse)
|
|
.or_else(handleError);
|
|
}
|
|
};
|
|
|
|
} // namespace rublon
|