Add sample handler with basic signature (#906)

This commit is contained in:
Eddy Ashton 2020-03-03 10:03:02 +00:00 коммит произвёл GitHub
Родитель d864021498
Коммит 38b4d0af40
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 151 добавлений и 37 удалений

Просмотреть файл

@ -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, &notifier](
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,