зеркало из https://github.com/microsoft/CCF.git
Add sample handler with basic signature (#906)
This commit is contained in:
Родитель
d864021498
Коммит
38b4d0af40
|
@ -65,6 +65,18 @@ Each function is installed as the handler for a specific RPC ``method``, optiona
|
|||
:lines: 1
|
||||
:dedent: 6
|
||||
|
||||
These handlers use the simple signature provided by the ``handler_adapter`` wrapper function, which pre-parses a JSON params object from the HTTP request body.
|
||||
|
||||
For direct access to the request and response objects, the handler signature should take a single ``RequestArgs&`` argument. An example of this is included in the logging app:
|
||||
|
||||
.. literalinclude:: ../../../src/apps/logging/logging.cpp
|
||||
:language: cpp
|
||||
:start-after: SNIPPET_START: log_record_prefix_cert
|
||||
:end-before: SNIPPET_END: log_record_prefix_cert
|
||||
:dedent: 6
|
||||
|
||||
This uses mbedtls to parse the caller's TLS certificate, and prefixes the logged message with the Subject field extracted from this certificate.
|
||||
|
||||
A handler can either be installed as:
|
||||
|
||||
- ``Write``: this handler can only be executed on the primary of the consensus network.
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the Apache 2.0 License.
|
||||
#pragma once
|
||||
|
||||
#include <fmt/format_header_only.h>
|
||||
#include <mbedtls/asn1.h>
|
||||
#include <valijson/validator.hpp>
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
template <>
|
||||
struct formatter<valijson::ValidationResults::Error>
|
||||
{
|
||||
template <typename ParseContext>
|
||||
constexpr auto parse(ParseContext& ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const valijson::ValidationResults::Error& e, FormatContext& ctx)
|
||||
{
|
||||
return format_to(
|
||||
ctx.begin(), "[{}] {}", fmt::join(e.context, ""), e.description);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct formatter<valijson::ValidationResults>
|
||||
{
|
||||
template <typename ParseContext>
|
||||
constexpr auto parse(ParseContext& ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const valijson::ValidationResults& vr, FormatContext& ctx)
|
||||
{
|
||||
return format_to(ctx.begin(), "{}", fmt::join(vr, "\n\t"));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct formatter<mbedtls_asn1_named_data>
|
||||
{
|
||||
template <typename ParseContext>
|
||||
constexpr auto parse(ParseContext& ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const mbedtls_asn1_named_data& n, FormatContext& ctx)
|
||||
{
|
||||
const mbedtls_asn1_named_data* current = &n;
|
||||
|
||||
format_to(ctx.out(), "[");
|
||||
|
||||
while (current != nullptr)
|
||||
{
|
||||
const auto oid = current->oid;
|
||||
const char* oid_name;
|
||||
mbedtls_oid_get_attr_short_name(&oid, &oid_name);
|
||||
|
||||
const auto val = current->val;
|
||||
|
||||
format_to(
|
||||
ctx.out(),
|
||||
"{}{}={}",
|
||||
(current == &n ? "" : ", "),
|
||||
oid_name,
|
||||
std::string_view((char const*)val.p, val.len));
|
||||
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return format_to(ctx.out(), "]");
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the Apache 2.0 License.
|
||||
#include "enclave/appinterface.h"
|
||||
#include "formatters.h"
|
||||
#include "logging_schema.h"
|
||||
#include "node/rpc/userfrontend.h"
|
||||
|
||||
|
@ -14,42 +15,6 @@ using namespace std;
|
|||
using namespace nlohmann;
|
||||
using namespace ccf;
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
template <>
|
||||
struct formatter<valijson::ValidationResults::Error>
|
||||
{
|
||||
template <typename ParseContext>
|
||||
constexpr auto parse(ParseContext& ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const valijson::ValidationResults::Error& e, FormatContext& ctx)
|
||||
{
|
||||
return format_to(
|
||||
ctx.begin(), "[{}] {}", fmt::join(e.context, ""), e.description);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct formatter<valijson::ValidationResults>
|
||||
{
|
||||
template <typename ParseContext>
|
||||
constexpr auto parse(ParseContext& ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const valijson::ValidationResults& vr, FormatContext& ctx)
|
||||
{
|
||||
return format_to(ctx.begin(), "{}", fmt::join(vr, "\n\t"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace ccfapp
|
||||
{
|
||||
struct Procs
|
||||
|
@ -59,6 +24,8 @@ namespace ccfapp
|
|||
|
||||
static constexpr auto LOG_RECORD_PUBLIC = "LOG_record_pub";
|
||||
static constexpr auto LOG_GET_PUBLIC = "LOG_get_pub";
|
||||
|
||||
static constexpr auto LOG_RECORD_PREFIX_CERT = "LOG_record_prefix_cert";
|
||||
};
|
||||
|
||||
// SNIPPET_START: errors
|
||||
|
@ -232,6 +199,36 @@ namespace ccfapp
|
|||
};
|
||||
// SNIPPET_END: get_public
|
||||
|
||||
// SNIPPET_START: log_record_prefix_cert
|
||||
auto log_record_prefix_cert = [this](RequestArgs& args) {
|
||||
mbedtls_x509_crt cert;
|
||||
mbedtls_x509_crt_init(&cert);
|
||||
|
||||
const auto& cert_data = args.rpc_ctx->session.caller_cert;
|
||||
const auto ret =
|
||||
mbedtls_x509_crt_parse(&cert, cert_data.data(), cert_data.size());
|
||||
|
||||
const auto body_j =
|
||||
nlohmann::json::parse(args.rpc_ctx->get_request_body());
|
||||
|
||||
const auto in = body_j.get<LoggingRecord::In>();
|
||||
if (in.msg.empty())
|
||||
{
|
||||
args.rpc_ctx->set_response_error(
|
||||
(int)LoggerErrors::MESSAGE_EMPTY,
|
||||
"Cannot record an empty log message");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto log_line = fmt::format("{}: {}", cert.subject, in.msg);
|
||||
auto view = args.tx.get_view(records);
|
||||
view->put(in.id, log_line);
|
||||
|
||||
args.rpc_ctx->set_response_result(true);
|
||||
return;
|
||||
};
|
||||
// SNIPPET_END: log_record_prefix_cert
|
||||
|
||||
install_with_auto_schema<LoggingRecord::In, bool>(
|
||||
Procs::LOG_RECORD, handler_adapter(record), Write);
|
||||
// SNIPPET: install_get
|
||||
|
@ -251,6 +248,8 @@ namespace ccfapp
|
|||
get_public_params_schema,
|
||||
get_public_result_schema);
|
||||
|
||||
install(Procs::LOG_RECORD_PREFIX_CERT, log_record_prefix_cert, Write);
|
||||
|
||||
nwt.signatures.set_global_hook([this, ¬ifier](
|
||||
kv::Version version,
|
||||
const Signatures::State& s,
|
||||
|
|
|
@ -51,6 +51,27 @@ def test_large_messages(network, args):
|
|||
return network
|
||||
|
||||
|
||||
@reqs.description("Write/Read with cert prefix")
|
||||
@reqs.supports_methods("LOG_record_prefix_cert", "LOG_get")
|
||||
def test_cert_prefix(network, args):
|
||||
if args.package == "liblogging":
|
||||
primary, _ = network.find_primary()
|
||||
|
||||
for user_id in network.initial_users:
|
||||
with primary.user_client(user_id) as c:
|
||||
log_id = 101
|
||||
msg = "This message will be prefixed"
|
||||
c.rpc("LOG_record_prefix_cert", {"id": log_id, "msg": msg})
|
||||
r = c.rpc("LOG_get", {"id": log_id})
|
||||
assert r.result is not None
|
||||
assert f"CN=user{user_id}" in r.result["msg"]
|
||||
|
||||
else:
|
||||
LOG.warning("Skipping test_cert_prefix as application is not C++")
|
||||
|
||||
return network
|
||||
|
||||
|
||||
@reqs.description("Testing forwarding on member and node frontends")
|
||||
@reqs.supports_methods("mkSign")
|
||||
@reqs.at_least_n_nodes(2)
|
||||
|
@ -138,6 +159,7 @@ def run(args):
|
|||
network = test_large_messages(network, args)
|
||||
network = test_forwarding_frontends(network, args)
|
||||
network = test_update_lua(network, args)
|
||||
network = test_cert_prefix(network, args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -218,7 +218,7 @@ class Node:
|
|||
with open(self.remote.get_sealed_secrets()) as s:
|
||||
return json.load(s)
|
||||
|
||||
def user_client(self, user_id=1, **kwargs):
|
||||
def user_client(self, user_id=0, **kwargs):
|
||||
return infra.clients.client(
|
||||
self.host,
|
||||
self.rpc_port,
|
||||
|
|
Загрузка…
Ссылка в новой задаче