rublon-ssh/PAM/ssh/include/rublon/core_handler.hpp
Bartosz Wieczorek 700845e17a refactor
2023-08-22 13:34:40 +02:00

102 lines
3.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;
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);
}
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}, http{[]() { return Response{}; }} {}
tl::expected< 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(const Response & response) const {
RapidJSONPMRStackAlloc< 4 * 1024 > alloc{};
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["result"].HasMember("exception")) {
const auto & exception = resp["result"]["exception"].GetString();
log(LogLevel::Error, "rublon Core exception %s", exception);
return tl::unexpected{CoreHandlerError{CoreHandlerError::CoreException, exception}};
}
return resp;
}
tl::expected< Document, Error > request(std::string_view path, const Document & body) const {
const auto validateSignature = [this](const auto & arg) { return this->validateSignature(arg); };
const auto validateResponse = [this](const auto & arg) { return this->validateResponse(arg); };
RapidJSONPMRStackAlloc< 1 * 1024 > alloc{};
StringBuffer jsonStr{&alloc};
Writer writer{jsonStr, &alloc};
body.Accept(writer);
std::byte _buffer[2 * 1024];
std::pmr::monotonic_buffer_resource mr{_buffer, sizeof(_buffer)};
Request request{mr};
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};
return http.request(uri, request)
.and_then(validateSignature)
.and_then(validateResponse)
.or_else([](const Error & e) -> tl::expected< Document, Error > { return tl::unexpected{e}; });
}
};
} // namespace rublon