зеркало из https://github.com/microsoft/CCF.git
Set keyId when signing HTTP requests from C++ (#2101)
This commit is contained in:
Родитель
550fb77d6a
Коммит
941d945360
|
@ -294,7 +294,7 @@ add_executable(
|
|||
use_client_mbedtls(scenario_perf_client)
|
||||
target_link_libraries(
|
||||
scenario_perf_client PRIVATE ${CMAKE_THREAD_LIBS_INIT} secp256k1.host
|
||||
http_parser.host
|
||||
http_parser.host ccfcrypto.host
|
||||
)
|
||||
install(TARGETS scenario_perf_client DESTINATION bin)
|
||||
|
||||
|
|
|
@ -14,7 +14,9 @@ add_client_exe(
|
|||
small_bank_client
|
||||
SRCS ${CMAKE_CURRENT_LIST_DIR}/clients/small_bank_client.cpp
|
||||
)
|
||||
target_link_libraries(small_bank_client PRIVATE secp256k1.host http_parser.host)
|
||||
target_link_libraries(
|
||||
small_bank_client PRIVATE secp256k1.host http_parser.host ccfcrypto.host
|
||||
)
|
||||
|
||||
# SmallBank application
|
||||
add_ccf_app(smallbank SRCS ${CMAKE_CURRENT_LIST_DIR}/app/smallbank.cpp)
|
||||
|
|
|
@ -40,6 +40,7 @@ protected:
|
|||
ws::ResponseParser ws_parser;
|
||||
std::optional<std::string> prefix;
|
||||
tls::KeyPairPtr key_pair = nullptr;
|
||||
std::string key_id = "Invalid";
|
||||
bool is_ws = false;
|
||||
|
||||
size_t next_send_id = 0;
|
||||
|
@ -80,7 +81,7 @@ protected:
|
|||
|
||||
if (key_pair != nullptr)
|
||||
{
|
||||
http::sign_request(r, key_pair);
|
||||
http::sign_request(r, key_pair, key_id);
|
||||
}
|
||||
|
||||
return r.build_request();
|
||||
|
@ -133,14 +134,17 @@ public:
|
|||
const std::string& host,
|
||||
const std::string& port,
|
||||
std::shared_ptr<tls::CA> node_ca = nullptr,
|
||||
std::shared_ptr<tls::Cert> cert = nullptr) :
|
||||
std::shared_ptr<tls::Cert> cert = nullptr,
|
||||
const std::string& key_id_ = "Invalid") :
|
||||
TlsClient(host, port, node_ca, cert),
|
||||
key_id(key_id_),
|
||||
parser(*this),
|
||||
ws_parser(*this)
|
||||
{}
|
||||
|
||||
HttpRpcTlsClient(const HttpRpcTlsClient& c) :
|
||||
TlsClient(c),
|
||||
key_id(c.key_id),
|
||||
parser(*this),
|
||||
ws_parser(*this)
|
||||
{}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
class TlsClient
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
std::string host;
|
||||
std::string port;
|
||||
std::shared_ptr<tls::CA> node_ca;
|
||||
|
|
|
@ -75,15 +75,6 @@ namespace ccf
|
|||
tx.get_read_only_view<CertDigests>(Tables::USER_DIGESTS);
|
||||
auto user_id = digests_view->get(signed_request->key_id);
|
||||
|
||||
// This should be removed once
|
||||
// https://github.com/microsoft/CCF/issues/2018 is completed
|
||||
if (!user_id.has_value())
|
||||
{
|
||||
auto user_certs_view =
|
||||
tx.get_read_only_view<CertDERs>(Tables::USER_CERT_DERS);
|
||||
user_id = user_certs_view->get(ctx->session->caller_cert);
|
||||
}
|
||||
|
||||
if (user_id.has_value())
|
||||
{
|
||||
Users users_table(Tables::USERS);
|
||||
|
@ -185,15 +176,6 @@ namespace ccf
|
|||
tx.get_read_only_view<CertDigests>(Tables::MEMBER_DIGESTS);
|
||||
auto member_id = digests_view->get(signed_request->key_id);
|
||||
|
||||
// This should be removed once
|
||||
// https://github.com/microsoft/CCF/issues/2018 is completed
|
||||
if (!member_id.has_value())
|
||||
{
|
||||
auto member_certs_view =
|
||||
tx.get_read_only_view<CertDERs>(Tables::MEMBER_CERT_DERS);
|
||||
member_id = member_certs_view->get(ctx->session->caller_cert);
|
||||
}
|
||||
|
||||
if (member_id.has_value())
|
||||
{
|
||||
Members members_table(Tables::MEMBERS);
|
||||
|
|
|
@ -67,12 +67,6 @@ namespace http
|
|||
return ret;
|
||||
}
|
||||
|
||||
struct SigningDetails
|
||||
{
|
||||
std::vector<uint8_t> to_sign;
|
||||
std::vector<uint8_t> signature;
|
||||
};
|
||||
|
||||
inline void add_digest_header(http::Request& request)
|
||||
{
|
||||
// Ensure digest is present and up-to-date
|
||||
|
@ -93,8 +87,8 @@ namespace http
|
|||
inline void sign_request(
|
||||
http::Request& request,
|
||||
const tls::KeyPairPtr& kp,
|
||||
const std::vector<std::string_view>& headers_to_sign,
|
||||
SigningDetails* details = nullptr)
|
||||
const std::string& key_id,
|
||||
const std::vector<std::string_view>& headers_to_sign)
|
||||
{
|
||||
add_digest_header(request);
|
||||
|
||||
|
@ -112,35 +106,29 @@ namespace http
|
|||
|
||||
const auto signature = kp->sign(to_sign.value());
|
||||
|
||||
// https://github.com/microsoft/CCF/issues/2018
|
||||
auto auth_value = fmt::format(
|
||||
"Signature "
|
||||
"keyId=\"ignored\",algorithm=\"{}\",headers=\"{}\",signature="
|
||||
"keyId=\"{}\",algorithm=\"{}\",headers=\"{}\",signature="
|
||||
"\"{}\"",
|
||||
key_id,
|
||||
auth::SIGN_ALGORITHM_HS_2019,
|
||||
fmt::format("{}", fmt::join(headers_to_sign, " ")),
|
||||
tls::b64_from_raw(signature.data(), signature.size()));
|
||||
|
||||
request.set_header(headers::AUTHORIZATION, auth_value);
|
||||
|
||||
if (details != nullptr)
|
||||
{
|
||||
details->to_sign = to_sign.value();
|
||||
details->signature = signature;
|
||||
}
|
||||
}
|
||||
|
||||
inline void sign_request(
|
||||
http::Request& request,
|
||||
const tls::KeyPairPtr& kp,
|
||||
SigningDetails* details = nullptr)
|
||||
const std::string& key_id)
|
||||
{
|
||||
std::vector<std::string_view> headers_to_sign;
|
||||
headers_to_sign.emplace_back(auth::SIGN_HEADER_REQUEST_TARGET);
|
||||
headers_to_sign.emplace_back(headers::DIGEST);
|
||||
headers_to_sign.emplace_back(headers::CONTENT_LENGTH);
|
||||
|
||||
sign_request(request, kp, headers_to_sign, details);
|
||||
sign_request(request, kp, key_id, headers_to_sign);
|
||||
}
|
||||
|
||||
// Implements verification of "Signature" scheme from
|
||||
|
|
|
@ -455,6 +455,7 @@ DOCTEST_TEST_CASE("Signatures")
|
|||
// Produce signed requests with some formatting variations, ensure we can
|
||||
// parse them
|
||||
auto kp = tls::make_key_pair();
|
||||
const std::string key_id = "UniqueIdentifierForThisKeypair";
|
||||
|
||||
http::Request request("/foo", HTTP_POST);
|
||||
request.set_query_param("param", "value");
|
||||
|
@ -486,7 +487,7 @@ DOCTEST_TEST_CASE("Signatures")
|
|||
headers_to_sign.emplace_back(http::auth::SIGN_HEADER_REQUEST_TARGET);
|
||||
headers_to_sign.emplace_back(http::headers::DIGEST);
|
||||
|
||||
http::sign_request(request, kp, headers_to_sign);
|
||||
http::sign_request(request, kp, key_id, headers_to_sign);
|
||||
|
||||
const auto serial_request = request.build_request();
|
||||
|
||||
|
@ -496,6 +497,7 @@ DOCTEST_TEST_CASE("Signatures")
|
|||
p.execute(serial_request.data(), serial_request.size());
|
||||
DOCTEST_REQUIRE(sp.signed_reqs.size() == 1);
|
||||
const auto& sr = sp.signed_reqs.back();
|
||||
DOCTEST_REQUIRE(sr.key_id == key_id);
|
||||
sp.signed_reqs.pop();
|
||||
}
|
||||
|
||||
|
@ -512,7 +514,7 @@ DOCTEST_TEST_CASE("Signatures")
|
|||
std::sort(headers_to_sign.begin(), headers_to_sign.end());
|
||||
while (true)
|
||||
{
|
||||
http::sign_request(request, kp, headers_to_sign);
|
||||
http::sign_request(request, kp, key_id, headers_to_sign);
|
||||
|
||||
const auto serial_request = request.build_request();
|
||||
|
||||
|
@ -522,6 +524,7 @@ DOCTEST_TEST_CASE("Signatures")
|
|||
p.execute(serial_request.data(), serial_request.size());
|
||||
DOCTEST_REQUIRE(sp.signed_reqs.size() == 1);
|
||||
const auto& sr = sp.signed_reqs.back();
|
||||
DOCTEST_REQUIRE(sr.key_id == key_id);
|
||||
sp.signed_reqs.pop();
|
||||
|
||||
const bool was_last_permutation =
|
||||
|
@ -542,7 +545,7 @@ DOCTEST_TEST_CASE("Signatures")
|
|||
headers_to_sign.emplace_back(header_it.first);
|
||||
}
|
||||
|
||||
http::sign_request(request, kp, headers_to_sign);
|
||||
http::sign_request(request, kp, key_id, headers_to_sign);
|
||||
|
||||
const auto& headers = request.get_headers();
|
||||
const auto auth_it = headers.find(http::headers::AUTHORIZATION);
|
||||
|
|
|
@ -23,21 +23,24 @@ namespace ccf
|
|||
std::shared_ptr<kv::Consensus> consensus;
|
||||
std::shared_ptr<enclave::RPCSessions> rpcsessions;
|
||||
std::shared_ptr<enclave::RPCMap> rpc_map;
|
||||
tls::KeyPairPtr node_sign_kp;
|
||||
tls::Pem node_cert;
|
||||
|
||||
public:
|
||||
JwtKeyAutoRefresh(
|
||||
size_t refresh_interval_s,
|
||||
NetworkState& network,
|
||||
std::shared_ptr<kv::Consensus> consensus,
|
||||
std::shared_ptr<enclave::RPCSessions> rpcsessions,
|
||||
std::shared_ptr<enclave::RPCMap> rpc_map,
|
||||
const std::shared_ptr<kv::Consensus>& consensus,
|
||||
const std::shared_ptr<enclave::RPCSessions>& rpcsessions,
|
||||
const std::shared_ptr<enclave::RPCMap>& rpc_map,
|
||||
const tls::KeyPairPtr& node_sign_kp,
|
||||
tls::Pem node_cert) :
|
||||
refresh_interval_s(refresh_interval_s),
|
||||
network(network),
|
||||
consensus(consensus),
|
||||
rpcsessions(rpcsessions),
|
||||
rpc_map(rpc_map),
|
||||
node_sign_kp(node_sign_kp),
|
||||
node_cert(node_cert)
|
||||
{}
|
||||
|
||||
|
@ -111,9 +114,13 @@ namespace ccf
|
|||
request.set_header(
|
||||
http::headers::CONTENT_TYPE, http::headervalues::contenttype::JSON);
|
||||
request.set_body(&body);
|
||||
// Need a custom authentication policy that accepts only node certs.
|
||||
// See https://github.com/microsoft/CCF/issues/1904
|
||||
// http::sign_request(request, node_sign_kp);
|
||||
|
||||
crypto::Sha256Hash hash;
|
||||
const auto contents = node_cert.contents();
|
||||
tls::do_hash(contents.data(), contents.size(), hash.h, MBEDTLS_MD_SHA256);
|
||||
const std::string key_id = fmt::format("{:02x}", fmt::join(hash.h, ""));
|
||||
|
||||
http::sign_request(request, node_sign_kp, key_id);
|
||||
auto packed = request.build_request();
|
||||
|
||||
auto node_session = std::make_shared<enclave::SessionContext>(
|
||||
|
|
|
@ -659,6 +659,7 @@ namespace ccf
|
|||
consensus,
|
||||
rpcsessions,
|
||||
rpc_map,
|
||||
node_sign_kp,
|
||||
node_cert);
|
||||
jwt_key_auto_refresh->start();
|
||||
|
||||
|
@ -1401,7 +1402,13 @@ namespace ccf
|
|||
http::headers::CONTENT_TYPE, http::headervalues::contenttype::JSON);
|
||||
|
||||
request.set_body(&body);
|
||||
http::sign_request(request, node_sign_kp);
|
||||
|
||||
crypto::Sha256Hash hash;
|
||||
const auto contents = node_cert.contents();
|
||||
tls::do_hash(contents.data(), contents.size(), hash.h, MBEDTLS_MD_SHA256);
|
||||
const std::string key_id = fmt::format("{:02x}", fmt::join(hash.h, ""));
|
||||
|
||||
http::sign_request(request, node_sign_kp, key_id);
|
||||
|
||||
return request.build_request();
|
||||
}
|
||||
|
|
|
@ -399,6 +399,7 @@ auto create_simple_request(
|
|||
}
|
||||
|
||||
http::Request create_signed_request(
|
||||
const tls::Pem& caller_cert,
|
||||
const http::Request& r = create_simple_request(),
|
||||
const std::vector<uint8_t>* body = nullptr)
|
||||
{
|
||||
|
@ -406,8 +407,12 @@ http::Request create_signed_request(
|
|||
|
||||
s.set_body(body);
|
||||
|
||||
http::SigningDetails details;
|
||||
http::sign_request(s, kp, &details);
|
||||
crypto::Sha256Hash hash;
|
||||
const auto contents = caller_cert.contents();
|
||||
tls::do_hash(contents.data(), contents.size(), hash.h, MBEDTLS_MD_SHA256);
|
||||
const std::string key_id = fmt::format("{:02x}", fmt::join(hash.h, ""));
|
||||
|
||||
http::sign_request(s, kp, key_id);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
@ -567,7 +572,7 @@ TEST_CASE("process with signatures")
|
|||
SUBCASE("endpoint does not require signature")
|
||||
{
|
||||
const auto simple_call = create_simple_request();
|
||||
const auto signed_call = create_signed_request(simple_call);
|
||||
const auto signed_call = create_signed_request(user_caller, simple_call);
|
||||
const auto serialized_simple_call = simple_call.build_request();
|
||||
const auto serialized_signed_call = signed_call.build_request();
|
||||
|
||||
|
@ -594,7 +599,7 @@ TEST_CASE("process with signatures")
|
|||
SUBCASE("endpoint requires signature")
|
||||
{
|
||||
const auto simple_call = create_simple_request("empty_function_signed");
|
||||
const auto signed_call = create_signed_request(simple_call);
|
||||
const auto signed_call = create_signed_request(user_caller, simple_call);
|
||||
const auto serialized_simple_call = simple_call.build_request();
|
||||
const auto serialized_signed_call = signed_call.build_request();
|
||||
|
||||
|
@ -785,9 +790,8 @@ TEST_CASE("MinimalEndpointFunction")
|
|||
const nlohmann::json j_body = {{"data", {"nested", "Some string"}},
|
||||
{"other", "Another string"}};
|
||||
const auto serialized_body = serdes::pack(j_body, pack_type);
|
||||
|
||||
auto signed_call = create_signed_request(echo_call, &serialized_body);
|
||||
const auto serialized_call = signed_call.build_request();
|
||||
echo_call.set_body(serialized_body.data(), serialized_body.size());
|
||||
const auto serialized_call = echo_call.build_request();
|
||||
|
||||
auto rpc_ctx = enclave::make_rpc_context(user_session, serialized_call);
|
||||
auto response = parse_response(frontend.process(rpc_ctx).value());
|
||||
|
@ -817,10 +821,8 @@ TEST_CASE("MinimalEndpointFunction")
|
|||
|
||||
{
|
||||
INFO("Calling get_caller");
|
||||
auto get_caller = create_simple_request("get_caller", pack_type);
|
||||
|
||||
const auto signed_call = create_signed_request(get_caller);
|
||||
const auto serialized_call = signed_call.build_request();
|
||||
const auto get_caller = create_simple_request("get_caller", pack_type);
|
||||
const auto serialized_call = get_caller.build_request();
|
||||
|
||||
auto rpc_ctx = enclave::make_rpc_context(user_session, serialized_call);
|
||||
auto response = parse_response(frontend.process(rpc_ctx).value());
|
||||
|
@ -834,9 +836,7 @@ TEST_CASE("MinimalEndpointFunction")
|
|||
{
|
||||
INFO("Calling failable, without failing");
|
||||
auto dont_fail = create_simple_request("failable");
|
||||
|
||||
const auto signed_call = create_signed_request(dont_fail);
|
||||
const auto serialized_call = signed_call.build_request();
|
||||
const auto serialized_call = dont_fail.build_request();
|
||||
|
||||
auto rpc_ctx = enclave::make_rpc_context(user_session, serialized_call);
|
||||
auto response = parse_response(frontend.process(rpc_ctx).value());
|
||||
|
@ -856,9 +856,8 @@ TEST_CASE("MinimalEndpointFunction")
|
|||
const nlohmann::json j_body = {
|
||||
{"error", {{"code", err}, {"message", msg}}}};
|
||||
const auto serialized_body = serdes::pack(j_body, default_pack);
|
||||
|
||||
const auto signed_call = create_signed_request(fail, &serialized_body);
|
||||
const auto serialized_call = signed_call.build_request();
|
||||
fail.set_body(serialized_body.data(), serialized_body.size());
|
||||
const auto serialized_call = fail.build_request();
|
||||
|
||||
auto rpc_ctx = enclave::make_rpc_context(user_session, serialized_call);
|
||||
auto response = parse_response(frontend.process(rpc_ctx).value());
|
||||
|
@ -1132,7 +1131,7 @@ TEST_CASE("Signed read requests can be executed on backup")
|
|||
auto backup_consensus = std::make_shared<kv::BackupStubConsensus>();
|
||||
network.tables->set_consensus(backup_consensus);
|
||||
|
||||
auto signed_call = create_signed_request();
|
||||
auto signed_call = create_signed_request(user_caller);
|
||||
auto serialized_signed_call = signed_call.build_request();
|
||||
auto rpc_ctx =
|
||||
enclave::make_rpc_context(user_session, serialized_signed_call);
|
||||
|
@ -1266,7 +1265,7 @@ TEST_CASE("Forwarding" * doctest::test_suite("forwarding"))
|
|||
INFO("Client signature on forwarded RPC is recorded by primary");
|
||||
|
||||
REQUIRE(channel_stub->is_empty());
|
||||
auto signed_call = create_signed_request();
|
||||
auto signed_call = create_signed_request(user_caller);
|
||||
auto serialized_signed_call = signed_call.build_request();
|
||||
auto signed_ctx =
|
||||
enclave::make_rpc_context(user_session, serialized_signed_call);
|
||||
|
|
|
@ -108,15 +108,21 @@ std::vector<uint8_t> create_signed_request(
|
|||
const json& params,
|
||||
const string& method_name,
|
||||
const tls::KeyPairPtr& kp_,
|
||||
const tls::Pem& caller,
|
||||
llhttp_method verb = HTTP_POST)
|
||||
{
|
||||
http::Request r(method_name, verb);
|
||||
|
||||
const auto body = params.is_null() ? std::vector<uint8_t>() :
|
||||
serdes::pack(params, default_pack);
|
||||
|
||||
r.set_body(&body);
|
||||
http::sign_request(r, kp_);
|
||||
|
||||
crypto::Sha256Hash hash;
|
||||
const auto contents = caller.contents();
|
||||
tls::do_hash(contents.data(), contents.size(), hash.h, MBEDTLS_MD_SHA256);
|
||||
const std::string key_id = fmt::format("{:02x}", fmt::join(hash.h, ""));
|
||||
|
||||
http::sign_request(r, kp_, key_id);
|
||||
|
||||
return r.build_request();
|
||||
}
|
||||
|
@ -199,7 +205,7 @@ auto activate(
|
|||
|
||||
StateDigest params;
|
||||
params.state_digest = ack.state_digest;
|
||||
const auto ack_req = create_signed_request(params, "ack", kp);
|
||||
const auto ack_req = create_signed_request(params, "ack", kp, caller);
|
||||
return frontend_process(frontend, ack_req, caller);
|
||||
}
|
||||
|
||||
|
@ -376,7 +382,8 @@ DOCTEST_TEST_CASE("Proposer ballot")
|
|||
)xxx");
|
||||
proposal.parameter["cert"] = proposed_member;
|
||||
proposal.parameter["encryption_pub_key"] = dummy_enc_pubk;
|
||||
const auto propose = create_signed_request(proposal, "proposals", kp);
|
||||
const auto propose =
|
||||
create_signed_request(proposal, "proposals", kp, proposer_cert);
|
||||
const auto r = frontend_process(frontend, propose, proposer_cert);
|
||||
|
||||
// the proposal should be accepted, but not succeed immediately
|
||||
|
@ -390,7 +397,10 @@ DOCTEST_TEST_CASE("Proposer ballot")
|
|||
DOCTEST_INFO("Second member votes for proposal");
|
||||
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_for}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote_for},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
voter_cert);
|
||||
const auto r = frontend_process(frontend, vote, voter_cert);
|
||||
|
||||
// The vote should not yet succeed
|
||||
|
@ -425,7 +435,10 @@ DOCTEST_TEST_CASE("Proposer ballot")
|
|||
DOCTEST_INFO("Proposer votes for");
|
||||
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_for}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote_for},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
proposer_cert);
|
||||
const auto r = frontend_process(frontend, vote, proposer_cert);
|
||||
|
||||
// The vote should now succeed
|
||||
|
@ -471,7 +484,8 @@ DOCTEST_TEST_CASE("Reject duplicate vote")
|
|||
)xxx");
|
||||
proposal.parameter["cert"] = proposed_member;
|
||||
proposal.parameter["encryption_pub_key"] = dummy_enc_pubk;
|
||||
const auto propose = create_signed_request(proposal, "proposals", kp);
|
||||
const auto propose =
|
||||
create_signed_request(proposal, "proposals", kp, proposer_cert);
|
||||
const auto r = frontend_process(frontend, propose, proposer_cert);
|
||||
|
||||
// the proposal should be accepted, but not succeed immediately
|
||||
|
@ -485,7 +499,10 @@ DOCTEST_TEST_CASE("Reject duplicate vote")
|
|||
DOCTEST_INFO("Proposer votes for");
|
||||
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_for}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote_for},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
proposer_cert);
|
||||
const auto r = frontend_process(frontend, vote, proposer_cert);
|
||||
|
||||
// The vote should now succeed
|
||||
|
@ -496,7 +513,10 @@ DOCTEST_TEST_CASE("Reject duplicate vote")
|
|||
DOCTEST_INFO("Proposer cannot vote again");
|
||||
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_against}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote_against},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
proposer_cert);
|
||||
check_error(
|
||||
frontend_process(frontend, vote, proposer_cert), HTTP_STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
@ -577,7 +597,8 @@ DOCTEST_TEST_CASE("Add new members until there are 7 then reject")
|
|||
proposal.parameter["cert"] = cert_pem;
|
||||
proposal.parameter["encryption_pub_key"] = dummy_enc_pubk;
|
||||
|
||||
const auto propose = create_signed_request(proposal, "proposals", kp);
|
||||
const auto propose =
|
||||
create_signed_request(proposal, "proposals", kp, member_cert);
|
||||
|
||||
{
|
||||
const auto r = frontend_process(frontend, propose, member_cert);
|
||||
|
@ -592,7 +613,10 @@ DOCTEST_TEST_CASE("Add new members until there are 7 then reject")
|
|||
// vote for own proposal
|
||||
Script vote_yes("return true");
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_yes}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote_yes},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
member_cert);
|
||||
const auto r = frontend_process(frontend, vote, member_cert);
|
||||
const auto result = parse_response_body<ProposalInfo>(r);
|
||||
DOCTEST_CHECK(result.state == ProposalState::OPEN);
|
||||
|
@ -620,7 +644,10 @@ DOCTEST_TEST_CASE("Add new members until there are 7 then reject")
|
|||
max_members));
|
||||
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_ballot}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote_ballot},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
voter_a_cert);
|
||||
|
||||
{
|
||||
const auto r = frontend_process(frontend, vote, voter_a_cert);
|
||||
|
@ -705,7 +732,7 @@ DOCTEST_TEST_CASE("Add new members until there are 7 then reject")
|
|||
StateDigest params;
|
||||
params.state_digest = ack0.state_digest;
|
||||
const auto send_stale_sig_req =
|
||||
create_signed_request(params, "ack", new_member->kp);
|
||||
create_signed_request(params, "ack", new_member->kp, new_member->cert);
|
||||
check_error(
|
||||
frontend_process(frontend, send_stale_sig_req, new_member->cert),
|
||||
HTTP_STATUS_BAD_REQUEST);
|
||||
|
@ -713,7 +740,7 @@ DOCTEST_TEST_CASE("Add new members until there are 7 then reject")
|
|||
// (5) sign new state digest and send it
|
||||
params.state_digest = ack1.state_digest;
|
||||
const auto send_good_sig_req =
|
||||
create_signed_request(params, "ack", new_member->kp);
|
||||
create_signed_request(params, "ack", new_member->kp, new_member->cert);
|
||||
const auto good_response =
|
||||
frontend_process(frontend, send_good_sig_req, new_member->cert);
|
||||
DOCTEST_CHECK(good_response.status == HTTP_STATUS_NO_CONTENT);
|
||||
|
@ -781,7 +808,7 @@ DOCTEST_TEST_CASE("Accept node")
|
|||
return Calls:call("trust_node", node_id)
|
||||
)xxx");
|
||||
const auto propose = create_signed_request(
|
||||
Propose::In{proposal, node_id}, "proposals", new_kp);
|
||||
Propose::In{proposal, node_id}, "proposals", new_kp, member_0_cert);
|
||||
const auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, member_0_cert));
|
||||
|
||||
|
@ -795,7 +822,8 @@ DOCTEST_TEST_CASE("Accept node")
|
|||
const auto vote = create_signed_request(
|
||||
Vote{vote_yes},
|
||||
fmt::format("proposals/{}/votes", trust_node_proposal_id),
|
||||
new_kp);
|
||||
new_kp,
|
||||
member_0_cert);
|
||||
const auto r = frontend_process(frontend, vote, member_0_cert);
|
||||
const auto result = parse_response_body<ProposalInfo>(r);
|
||||
DOCTEST_CHECK(result.state == ProposalState::OPEN);
|
||||
|
@ -810,7 +838,8 @@ DOCTEST_TEST_CASE("Accept node")
|
|||
const auto vote = create_signed_request(
|
||||
Vote{vote_ballot},
|
||||
fmt::format("proposals/{}/votes", trust_node_proposal_id),
|
||||
kp);
|
||||
kp,
|
||||
member_1_cert);
|
||||
|
||||
check_result_state(
|
||||
frontend_process(frontend, vote, member_1_cert), ProposalState::ACCEPTED);
|
||||
|
@ -833,7 +862,7 @@ DOCTEST_TEST_CASE("Accept node")
|
|||
return Calls:call("retire_node", node_id)
|
||||
)xxx");
|
||||
const auto propose = create_signed_request(
|
||||
Propose::In{proposal, node_id}, "proposals", new_kp);
|
||||
Propose::In{proposal, node_id}, "proposals", new_kp, member_0_cert);
|
||||
const auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, member_0_cert));
|
||||
|
||||
|
@ -847,7 +876,8 @@ DOCTEST_TEST_CASE("Accept node")
|
|||
const auto vote = create_signed_request(
|
||||
Vote{vote_yes},
|
||||
fmt::format("proposals/{}/votes", retire_node_proposal_id),
|
||||
new_kp);
|
||||
new_kp,
|
||||
member_0_cert);
|
||||
const auto r = frontend_process(frontend, vote, member_0_cert);
|
||||
const auto result = parse_response_body<ProposalInfo>(r);
|
||||
DOCTEST_CHECK(result.state == ProposalState::OPEN);
|
||||
|
@ -859,7 +889,8 @@ DOCTEST_TEST_CASE("Accept node")
|
|||
const auto vote = create_signed_request(
|
||||
Vote{vote_ballot},
|
||||
fmt::format("proposals/{}/votes", retire_node_proposal_id),
|
||||
kp);
|
||||
kp,
|
||||
member_1_cert);
|
||||
check_result_state(
|
||||
frontend_process(frontend, vote, member_1_cert), ProposalState::ACCEPTED);
|
||||
}
|
||||
|
@ -880,7 +911,7 @@ DOCTEST_TEST_CASE("Accept node")
|
|||
return Calls:call("trust_node", node_id)
|
||||
)xxx");
|
||||
const auto propose = create_signed_request(
|
||||
Propose::In{proposal, node_id}, "proposals", new_kp);
|
||||
Propose::In{proposal, node_id}, "proposals", new_kp, member_0_cert);
|
||||
const auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, member_0_cert));
|
||||
|
||||
|
@ -888,11 +919,15 @@ DOCTEST_TEST_CASE("Accept node")
|
|||
auto vote = create_signed_request(
|
||||
Vote{vote_ballot},
|
||||
fmt::format("proposals/{}/votes", r.proposal_id),
|
||||
new_kp);
|
||||
new_kp,
|
||||
member_0_cert);
|
||||
frontend_process(frontend, vote, member_0_cert);
|
||||
|
||||
vote = create_signed_request(
|
||||
Vote{vote_ballot}, fmt::format("proposals/{}/votes", r.proposal_id), kp);
|
||||
Vote{vote_ballot},
|
||||
fmt::format("proposals/{}/votes", r.proposal_id),
|
||||
kp,
|
||||
member_1_cert);
|
||||
check_result_state(
|
||||
frontend_process(frontend, vote, member_1_cert), ProposalState::FAILED);
|
||||
}
|
||||
|
@ -904,7 +939,7 @@ DOCTEST_TEST_CASE("Accept node")
|
|||
return Calls:call("retire_node", node_id)
|
||||
)xxx");
|
||||
const auto propose = create_signed_request(
|
||||
Propose::In{proposal, node_id}, "proposals", new_kp);
|
||||
Propose::In{proposal, node_id}, "proposals", new_kp, member_0_cert);
|
||||
const auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, member_0_cert));
|
||||
|
||||
|
@ -912,11 +947,15 @@ DOCTEST_TEST_CASE("Accept node")
|
|||
auto vote = create_signed_request(
|
||||
Vote{vote_ballot},
|
||||
fmt::format("proposals/{}/votes", r.proposal_id),
|
||||
new_kp);
|
||||
new_kp,
|
||||
member_0_cert);
|
||||
frontend_process(frontend, vote, member_0_cert);
|
||||
|
||||
vote = create_signed_request(
|
||||
Vote{vote_ballot}, fmt::format("proposals/{}/votes", r.proposal_id), kp);
|
||||
Vote{vote_ballot},
|
||||
fmt::format("proposals/{}/votes", r.proposal_id),
|
||||
kp,
|
||||
member_1_cert);
|
||||
check_result_state(
|
||||
frontend_process(frontend, vote, member_1_cert), ProposalState::FAILED);
|
||||
}
|
||||
|
@ -950,7 +989,8 @@ ProposalInfo test_raw_writes(
|
|||
const auto proposal_id = 0ul;
|
||||
{
|
||||
const uint8_t proposer_id = 0;
|
||||
const auto propose = create_signed_request(proposal, "proposals", kp);
|
||||
const auto propose =
|
||||
create_signed_request(proposal, "proposals", kp, member_certs[0]);
|
||||
const auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, member_certs[0]));
|
||||
|
||||
|
@ -967,7 +1007,10 @@ ProposalInfo test_raw_writes(
|
|||
{
|
||||
const Script vote("return false");
|
||||
const auto vote_serialized = create_signed_request(
|
||||
Vote{vote}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
member_certs[i]);
|
||||
|
||||
check_result_state(
|
||||
frontend_process(frontend, vote_serialized, member_certs[i]),
|
||||
|
@ -980,7 +1023,10 @@ ProposalInfo test_raw_writes(
|
|||
{
|
||||
const Script vote("return true");
|
||||
const auto vote_serialized = create_signed_request(
|
||||
Vote{vote}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
member_certs[i]);
|
||||
if (info.state == ProposalState::OPEN)
|
||||
{
|
||||
info = parse_response_body<ProposalInfo>(
|
||||
|
@ -1131,8 +1177,8 @@ DOCTEST_TEST_CASE("Remove proposal")
|
|||
}
|
||||
|
||||
{
|
||||
const auto propose =
|
||||
create_signed_request(Propose::In{proposal_script, 0}, "proposals", kp);
|
||||
const auto propose = create_signed_request(
|
||||
Propose::In{proposal_script, 0}, "proposals", kp, member_cert);
|
||||
const auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, member_cert));
|
||||
|
||||
|
@ -1153,7 +1199,10 @@ DOCTEST_TEST_CASE("Remove proposal")
|
|||
DOCTEST_SUBCASE("Attempt withdraw proposal with non existing id")
|
||||
{
|
||||
const auto withdraw = create_signed_request(
|
||||
nullptr, fmt::format("proposals/{}/withdraw", wrong_proposal_id), kp);
|
||||
nullptr,
|
||||
fmt::format("proposals/{}/withdraw", wrong_proposal_id),
|
||||
kp,
|
||||
member_cert);
|
||||
|
||||
check_error(
|
||||
frontend_process(frontend, withdraw, member_cert),
|
||||
|
@ -1163,7 +1212,10 @@ DOCTEST_TEST_CASE("Remove proposal")
|
|||
DOCTEST_SUBCASE("Attempt withdraw proposal that you didn't propose")
|
||||
{
|
||||
const auto withdraw = create_signed_request(
|
||||
nullptr, fmt::format("proposals/{}/withdraw", proposal_id), caller.kp);
|
||||
nullptr,
|
||||
fmt::format("proposals/{}/withdraw", proposal_id),
|
||||
caller.kp,
|
||||
cert);
|
||||
|
||||
check_error(
|
||||
frontend_process(frontend, withdraw, cert), HTTP_STATUS_FORBIDDEN);
|
||||
|
@ -1172,7 +1224,10 @@ DOCTEST_TEST_CASE("Remove proposal")
|
|||
DOCTEST_SUBCASE("Successfully withdraw proposal")
|
||||
{
|
||||
const auto withdraw = create_signed_request(
|
||||
nullptr, fmt::format("proposals/{}/withdraw", proposal_id), kp);
|
||||
nullptr,
|
||||
fmt::format("proposals/{}/withdraw", proposal_id),
|
||||
kp,
|
||||
member_cert);
|
||||
|
||||
check_result_state(
|
||||
frontend_process(frontend, withdraw, member_cert),
|
||||
|
@ -1215,8 +1270,8 @@ DOCTEST_TEST_CASE("Vetoed proposal gets rejected")
|
|||
return Calls:call("new_user", user_cert)
|
||||
)xxx");
|
||||
|
||||
const auto propose =
|
||||
create_signed_request(Propose::In{proposal, user_cert}, "proposals", kp);
|
||||
const auto propose = create_signed_request(
|
||||
Propose::In{proposal, user_cert}, "proposals", kp, voter_a_cert);
|
||||
|
||||
const auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, voter_a_cert));
|
||||
|
@ -1227,7 +1282,10 @@ DOCTEST_TEST_CASE("Vetoed proposal gets rejected")
|
|||
DOCTEST_INFO("Member vetoes proposal");
|
||||
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_against}, fmt::format("proposals/{}/votes", r.proposal_id), kp);
|
||||
Vote{vote_against},
|
||||
fmt::format("proposals/{}/votes", r.proposal_id),
|
||||
kp,
|
||||
voter_b_cert);
|
||||
const auto r = frontend_process(frontend, vote, voter_b_cert);
|
||||
|
||||
check_result_state(r, ProposalState::REJECTED);
|
||||
|
@ -1271,8 +1329,8 @@ DOCTEST_TEST_CASE("Add and remove user via proposed calls")
|
|||
)xxx");
|
||||
|
||||
const auto user_cert = kp->self_sign("CN=new user");
|
||||
const auto propose =
|
||||
create_signed_request(Propose::In{proposal, user_cert}, "proposals", kp);
|
||||
const auto propose = create_signed_request(
|
||||
Propose::In{proposal, user_cert}, "proposals", kp, member_cert);
|
||||
|
||||
auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, member_cert));
|
||||
|
@ -1281,7 +1339,10 @@ DOCTEST_TEST_CASE("Add and remove user via proposed calls")
|
|||
// vote for own proposal
|
||||
Script vote_yes("return true");
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_yes}, fmt::format("proposals/{}/votes", r.proposal_id), kp);
|
||||
Vote{vote_yes},
|
||||
fmt::format("proposals/{}/votes", r.proposal_id),
|
||||
kp,
|
||||
member_cert);
|
||||
r = parse_response_body<ProposalInfo>(
|
||||
frontend_process(frontend, vote, member_cert));
|
||||
|
||||
|
@ -1306,8 +1367,8 @@ DOCTEST_TEST_CASE("Add and remove user via proposed calls")
|
|||
return Calls:call("remove_user", user_id)
|
||||
)xxx");
|
||||
|
||||
const auto propose =
|
||||
create_signed_request(Propose::In{proposal, 0}, "proposals", kp);
|
||||
const auto propose = create_signed_request(
|
||||
Propose::In{proposal, 0}, "proposals", kp, member_cert);
|
||||
|
||||
auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, member_cert));
|
||||
|
@ -1316,7 +1377,10 @@ DOCTEST_TEST_CASE("Add and remove user via proposed calls")
|
|||
// vote for own proposal
|
||||
Script vote_yes("return true");
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_yes}, fmt::format("proposals/{}/votes", r.proposal_id), kp);
|
||||
Vote{vote_yes},
|
||||
fmt::format("proposals/{}/votes", r.proposal_id),
|
||||
kp,
|
||||
member_cert);
|
||||
r = parse_response_body<ProposalInfo>(
|
||||
frontend_process(frontend, vote, member_cert));
|
||||
|
||||
|
@ -1395,7 +1459,8 @@ DOCTEST_TEST_CASE(
|
|||
proposal.parameter["cert"] = proposed_member;
|
||||
proposal.parameter["encryption_pub_key"] = dummy_enc_pubk;
|
||||
|
||||
const auto propose = create_signed_request(proposal, "proposals", kp);
|
||||
const auto propose =
|
||||
create_signed_request(proposal, "proposals", kp, members[proposer_id]);
|
||||
const auto r = parse_response_body<Propose::Out>(frontend_process(
|
||||
frontend,
|
||||
propose,
|
||||
|
@ -1410,7 +1475,10 @@ DOCTEST_TEST_CASE(
|
|||
DOCTEST_INFO("First member votes");
|
||||
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_for}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote_for},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
members[proposer_id]);
|
||||
const auto r = frontend_process(frontend, vote, members[proposer_id]);
|
||||
|
||||
check_result_state(r, ProposalState::OPEN);
|
||||
|
@ -1420,7 +1488,10 @@ DOCTEST_TEST_CASE(
|
|||
DOCTEST_INFO("Operator votes, but without effect");
|
||||
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_for}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote_for},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
operator_cert);
|
||||
const auto r = frontend_process(frontend, vote, operator_cert);
|
||||
|
||||
check_result_state(r, ProposalState::OPEN);
|
||||
|
@ -1430,7 +1501,10 @@ DOCTEST_TEST_CASE(
|
|||
DOCTEST_INFO("Second member votes for proposal, which passes");
|
||||
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_for}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote_for},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
members[voter_id]);
|
||||
const auto r = frontend_process(frontend, vote, members[voter_id]);
|
||||
|
||||
check_result_state(r, ProposalState::ACCEPTED);
|
||||
|
@ -1526,8 +1600,8 @@ DOCTEST_TEST_CASE("Passing operator change" * doctest::test_suite("operator"))
|
|||
return Calls:call("trust_node", node_id)
|
||||
)xxx");
|
||||
|
||||
const auto propose =
|
||||
create_signed_request(Propose::In{proposal, node_id}, "proposals", kp);
|
||||
const auto propose = create_signed_request(
|
||||
Propose::In{proposal, node_id}, "proposals", kp, operator_cert);
|
||||
const auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, operator_cert));
|
||||
|
||||
|
@ -1561,7 +1635,8 @@ DOCTEST_TEST_CASE("Passing operator change" * doctest::test_suite("operator"))
|
|||
proposal.parameter["cert"] = new_operator_cert;
|
||||
proposal.parameter["member_data"] = operator_member_data();
|
||||
|
||||
const auto propose = create_signed_request(proposal, "proposals", kp);
|
||||
const auto propose =
|
||||
create_signed_request(proposal, "proposals", kp, operator_cert);
|
||||
const auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, operator_cert));
|
||||
|
||||
|
@ -1576,8 +1651,8 @@ DOCTEST_TEST_CASE("Passing operator change" * doctest::test_suite("operator"))
|
|||
|
||||
StateDigest params;
|
||||
params.state_digest = ack.state_digest;
|
||||
const auto ack_req =
|
||||
create_signed_request(params, "ack", new_operator_kp);
|
||||
const auto ack_req = create_signed_request(
|
||||
params, "ack", new_operator_kp, new_operator_cert);
|
||||
const auto resp = frontend_process(frontend, ack_req, new_operator_cert);
|
||||
}
|
||||
}
|
||||
|
@ -1588,8 +1663,8 @@ DOCTEST_TEST_CASE("Passing operator change" * doctest::test_suite("operator"))
|
|||
proposal.script = fmt::format(
|
||||
R"xxx(return Calls:call("retire_member", {}))xxx", operator_id);
|
||||
|
||||
const auto propose =
|
||||
create_signed_request(proposal, "proposals", new_operator_kp);
|
||||
const auto propose = create_signed_request(
|
||||
proposal, "proposals", new_operator_kp, new_operator_cert);
|
||||
const auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, new_operator_cert));
|
||||
|
||||
|
@ -1613,8 +1688,8 @@ DOCTEST_TEST_CASE("Passing operator change" * doctest::test_suite("operator"))
|
|||
proposal.parameter["member_data"] =
|
||||
nullptr; // blank member_data => not an operator
|
||||
|
||||
const auto propose =
|
||||
create_signed_request(proposal, "proposals", new_operator_kp);
|
||||
const auto propose = create_signed_request(
|
||||
proposal, "proposals", new_operator_kp, new_operator_cert);
|
||||
const auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, new_operator_cert));
|
||||
|
||||
|
@ -1630,8 +1705,8 @@ DOCTEST_TEST_CASE("Passing operator change" * doctest::test_suite("operator"))
|
|||
proposal.script = fmt::format(
|
||||
R"xxx(return Calls:call("retire_member", {}))xxx", normal_member_id);
|
||||
|
||||
const auto propose =
|
||||
create_signed_request(proposal, "proposals", new_operator_kp);
|
||||
const auto propose = create_signed_request(
|
||||
proposal, "proposals", new_operator_kp, new_operator_cert);
|
||||
const auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, new_operator_cert));
|
||||
|
||||
|
@ -1705,8 +1780,8 @@ DOCTEST_TEST_CASE(
|
|||
return Calls:call("trust_node", node_id)
|
||||
)xxx");
|
||||
|
||||
const auto propose =
|
||||
create_signed_request(Propose::In{proposal, node_id}, "proposals", kp);
|
||||
const auto propose = create_signed_request(
|
||||
Propose::In{proposal, node_id}, "proposals", kp, proposer_cert);
|
||||
const auto r = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, propose, proposer_cert));
|
||||
|
||||
|
@ -1718,7 +1793,10 @@ DOCTEST_TEST_CASE(
|
|||
DOCTEST_INFO("Member votes against");
|
||||
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_against}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote_against},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
proposer_cert);
|
||||
const auto r = frontend_process(frontend, vote, proposer_cert);
|
||||
|
||||
check_result_state(r, ProposalState::OPEN);
|
||||
|
@ -1731,7 +1809,10 @@ DOCTEST_TEST_CASE(
|
|||
DOCTEST_INFO("First member votes for proposal");
|
||||
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_for}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote_for},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
members[first_voter_id]);
|
||||
const auto r = frontend_process(frontend, vote, members[first_voter_id]);
|
||||
|
||||
check_result_state(r, ProposalState::OPEN);
|
||||
|
@ -1741,7 +1822,10 @@ DOCTEST_TEST_CASE(
|
|||
DOCTEST_INFO("Second member votes for proposal");
|
||||
|
||||
const auto vote = create_signed_request(
|
||||
Vote{vote_for}, fmt::format("proposals/{}/votes", proposal_id), kp);
|
||||
Vote{vote_for},
|
||||
fmt::format("proposals/{}/votes", proposal_id),
|
||||
kp,
|
||||
members[second_voter_id]);
|
||||
const auto r = frontend_process(frontend, vote, members[second_voter_id]);
|
||||
|
||||
check_result_state(r, ProposalState::ACCEPTED);
|
||||
|
@ -1841,7 +1925,7 @@ DOCTEST_TEST_CASE("User data")
|
|||
)xxx",
|
||||
user_id);
|
||||
const auto proposal_serialized =
|
||||
create_signed_request(proposal, "proposals", kp);
|
||||
create_signed_request(proposal, "proposals", kp, member_cert);
|
||||
const auto propose_response = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, proposal_serialized, member_cert));
|
||||
|
||||
|
@ -1853,7 +1937,8 @@ DOCTEST_TEST_CASE("User data")
|
|||
const auto vote = create_signed_request(
|
||||
Vote{vote_yes},
|
||||
fmt::format("proposals/{}/votes", propose_response.proposal_id),
|
||||
kp);
|
||||
kp,
|
||||
member_cert);
|
||||
const auto r = frontend_process(frontend, vote, member_cert);
|
||||
const auto result = parse_response_body<ProposalInfo>(r);
|
||||
DOCTEST_CHECK(result.state == ProposalState::ACCEPTED);
|
||||
|
@ -1878,7 +1963,7 @@ DOCTEST_TEST_CASE("User data")
|
|||
proposal.parameter["id"] = user_id;
|
||||
proposal.parameter["data"] = user_data_string;
|
||||
const auto proposal_serialized =
|
||||
create_signed_request(proposal, "proposals", kp);
|
||||
create_signed_request(proposal, "proposals", kp, member_cert);
|
||||
const auto propose_response = parse_response_body<Propose::Out>(
|
||||
frontend_process(frontend, proposal_serialized, member_cert));
|
||||
DOCTEST_CHECK(propose_response.state == ProposalState::OPEN);
|
||||
|
@ -1889,7 +1974,8 @@ DOCTEST_TEST_CASE("User data")
|
|||
const auto vote = create_signed_request(
|
||||
Vote{vote_yes},
|
||||
fmt::format("proposals/{}/votes", propose_response.proposal_id),
|
||||
kp);
|
||||
kp,
|
||||
member_cert);
|
||||
const auto r = frontend_process(frontend, vote, member_cert);
|
||||
const auto result = parse_response_body<ProposalInfo>(r);
|
||||
DOCTEST_CHECK(result.state == ProposalState::ACCEPTED);
|
||||
|
|
|
@ -207,27 +207,8 @@ namespace client
|
|||
|
||||
private:
|
||||
tls::Pem key = {};
|
||||
std::shared_ptr<tls::Cert> tls_cert;
|
||||
|
||||
// Create tls_cert if it doesn't exist, and return it
|
||||
bool get_cert()
|
||||
{
|
||||
if (tls_cert == nullptr)
|
||||
{
|
||||
const auto raw_cert = files::slurp(options.cert_file);
|
||||
const auto raw_key = files::slurp(options.key_file);
|
||||
const auto ca = files::slurp(options.ca_file);
|
||||
|
||||
key = tls::Pem(raw_key);
|
||||
|
||||
tls_cert = std::make_shared<tls::Cert>(
|
||||
std::make_shared<tls::CA>(ca), raw_cert, key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
std::string key_id = "Invalid";
|
||||
std::shared_ptr<tls::Cert> tls_cert = nullptr;
|
||||
|
||||
// Process reply to an RPC. Records time reply was received. Calls
|
||||
// check_response for derived-overridable validation
|
||||
|
@ -308,23 +289,42 @@ namespace client
|
|||
bool force_unsigned = false, bool upgrade = false)
|
||||
{
|
||||
// Create a cert if this is our first rpc_connection
|
||||
const bool is_first = get_cert();
|
||||
const bool is_first_time = tls_cert == nullptr;
|
||||
|
||||
if (is_first_time)
|
||||
{
|
||||
const auto raw_cert = files::slurp(options.cert_file);
|
||||
const auto raw_key = files::slurp(options.key_file);
|
||||
const auto ca = files::slurp(options.ca_file);
|
||||
|
||||
key = tls::Pem(raw_key);
|
||||
|
||||
crypto::Sha256Hash hash;
|
||||
tls::do_hash(
|
||||
raw_cert.data(), raw_cert.size(), hash.h, MBEDTLS_MD_SHA256);
|
||||
key_id = fmt::format("{:02x}", fmt::join(hash.h, ""));
|
||||
|
||||
tls_cert = std::make_shared<tls::Cert>(
|
||||
std::make_shared<tls::CA>(ca), raw_cert, key);
|
||||
}
|
||||
|
||||
auto conn = std::make_shared<RpcTlsClient>(
|
||||
options.server_address.hostname,
|
||||
options.server_address.port,
|
||||
nullptr,
|
||||
tls_cert);
|
||||
tls_cert,
|
||||
key_id);
|
||||
|
||||
if (options.sign && !force_unsigned)
|
||||
{
|
||||
LOG_INFO_FMT("Creating key pair");
|
||||
conn->create_key_pair(key);
|
||||
}
|
||||
|
||||
conn->set_prefix("app");
|
||||
|
||||
// Report ciphersuite of first client (assume it is the same for each)
|
||||
if (is_first)
|
||||
if (is_first_time)
|
||||
{
|
||||
LOG_DEBUG_FMT(
|
||||
"Connected to server via TLS ({})", conn->get_ciphersuite_name());
|
||||
|
|
Загрузка…
Ссылка в новой задаче