From 09b690dde964117882793bfb9a11162d9449e113 Mon Sep 17 00:00:00 2001 From: Rich Nistuk Date: Mon, 3 Sep 2018 15:40:15 -0700 Subject: [PATCH] Fixing broken unit tests to incorporte changes to raft due to addition of the security flag --- CMakeLists.txt | 2 -- bootstrap/bootstrap_peers.cpp | 2 +- mocks/mock_raft_base.hpp | 2 ++ raft/raft.cpp | 63 ++++++++++++++++++++++------------- raft/raft.hpp | 11 ++++-- raft/raft_base.hpp | 10 ++++++ raft/raft_log.hpp | 8 ++--- raft/test/raft_test.cpp | 55 ++++++++++++++++++++++++++---- utils/crypto.hpp | 2 +- utils/test/utils_test.cpp | 2 +- 10 files changed, 116 insertions(+), 41 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17a39340..bda1fc34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,8 +52,6 @@ find_package( if(Boost_FOUND) message(STATUS "Boost: ${Boost_INCLUDE_DIRS}") - - message(STATUS "***boost root ${BOOST_ROOT}") include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) endif() diff --git a/bootstrap/bootstrap_peers.cpp b/bootstrap/bootstrap_peers.cpp index 5c32a4b7..6061bc79 100644 --- a/bootstrap/bootstrap_peers.cpp +++ b/bootstrap/bootstrap_peers.cpp @@ -108,7 +108,7 @@ bootstrap_peers::initialize_peer_list(const Json::Value& root, bzn::peers_list_t if (this->is_security_enabled()) { - // At this point we cannot validate uuid's as we do not have + // At this point we cannot validate uuids as we do not have // signatures for all of them, so we simply ignore blacklisted // uuids if(bzn::utils::blacklist::is_blacklisted(uuid)) diff --git a/mocks/mock_raft_base.hpp b/mocks/mock_raft_base.hpp index c21c6b2d..38471b21 100644 --- a/mocks/mock_raft_base.hpp +++ b/mocks/mock_raft_base.hpp @@ -31,6 +31,8 @@ namespace bzn bool(const bzn::message& msg, const bzn::log_entry_type entry_type)); MOCK_METHOD1(register_commit_handler, void(bzn::raft_base::commit_handler handler)); + MOCK_METHOD0(get_security_enabled, + bool()); }; } // namespace bzn \ No newline at end of file diff --git a/raft/raft.cpp b/raft/raft.cpp index a6f01dc7..0ab653b6 100644 --- a/raft/raft.cpp +++ b/raft/raft.cpp @@ -46,13 +46,13 @@ raft::raft( const bzn::peers_list_t& peers, bzn::uuid_t uuid, const std::string state_dir, size_t maximum_raft_storage, - bool whitelist_enabled + bool security_enabled ) :timer(io_context->make_unique_steady_timer()) ,uuid(std::move(uuid)) ,node(std::move(node)) ,state_dir(std::move(state_dir)) - ,whitelist_enabled(whitelist_enabled) + ,security_enabled(security_enabled) { // we must have a list of peers! if (peers.empty()) @@ -424,6 +424,34 @@ raft::send_session_error_message(std::shared_ptr session, con } +bool +raft::validate_new_peer(std::shared_ptr session, const bzn::message &peer) +{ + // uuid's are signed as text files, so we must append the end of line character + const auto uuid{this->get_uuid() + "\x0a"}; + if (!peer.isMember("signature") || peer["signature"].asString().empty()) + { + this->send_session_error_message(session, ERROR_INVALID_SIGNATURE); + return false; + } + + const auto signature{peer["signature"].asString()}; + if(!bzn::utils::crypto::verify_signature(bzn::utils::crypto::retrieve_bluzelle_public_key_from_contract(), signature, uuid)) + { + this->send_session_error_message(session, ERROR_UNABLE_TO_VALIDATE_UUID); + return false; + } + + if (utils::blacklist::is_blacklisted(this->get_uuid())) + { + this->send_session_error_message(session, ERROR_PEER_HAS_BEEN_BLACKLISTED); + return false; + } + return true; + +} + + void raft::handle_add_peer(std::shared_ptr session, const bzn::message &peer) { @@ -459,33 +487,22 @@ raft::handle_add_peer(std::shared_ptr session, const bzn::mes return; } - if (this->whitelist_enabled) + if (this->security_enabled && !this->validate_new_peer(session, peer)) { - // uuid's are signed as text files, so we must append the end of line character - const auto uuid{this->get_uuid() + "\x0a"}; - if (!peer.isMember("signature") || peer["signature"].asString().empty()) - { - this->send_session_error_message(session, ERROR_INVALID_SIGNATURE); - return; - } - - const auto signature{peer["signature"].asString()}; - if(!bzn::utils::crypto::verify_signature(bzn::utils::crypto::retrieve_bluzelle_public_key_from_contract(), signature, uuid)) - { - this->send_session_error_message(session, ERROR_UNABLE_TO_VALIDATE_UUID); - return; - } - - if (utils::blacklist::is_blacklisted(this->get_uuid())) - { - this->send_session_error_message(session, ERROR_PEER_HAS_BEEN_BLACKLISTED); - return; - } + return; } this->peer_match_index[peer["uuid"].asString()] = 1; this->append_log_unsafe(this->create_joint_quorum_by_adding_peer(last_quorum_entry.msg, peer), bzn::log_entry_type::joint_quorum); + + bzn::message msg; + msg["bzn-api"] = "raft"; + msg["cmd"] = "add_peer"; + msg["response"] = bzn::message(); + msg["response"]["from"] = this->get_uuid(); + msg["response"]["msg"] = SUCCESS_PEER_ADDED_TO_SWARM; + session->send_message(std::make_shared(msg), true); return; } diff --git a/raft/raft.hpp b/raft/raft.hpp index 136e3d6e..ef8ea1f5 100644 --- a/raft/raft.hpp +++ b/raft/raft.hpp @@ -42,6 +42,7 @@ namespace const std::string ERROR_PEER_HAS_BEEN_BLACKLISTED = "ERROR_PEER_HAS_BEEN_BLACKLISTED"; const std::string ERROR_INVALID_SIGNATURE = "ERROR_INVALID_SIGNATURE"; const std::string ERROR_UNABLE_TO_VALIDATE_UUID ="ERROR_UNABLE_TO_VALIDATE_UUID"; + const std::string SUCCESS_PEER_ADDED_TO_SWARM = "SUCCESS_PEER_ADDED_TO_SWARM"; } @@ -56,8 +57,8 @@ namespace bzn const bzn::peers_list_t& peers, bzn::uuid_t uuid, const std::string state_dir, - size_t maximum_raft_storage = bzn::raft_log::DEFAULT_MAX_STORAGE_SIZE, - bool whitelist_enabled = false); + size_t maximum_raft_storage = bzn::DEFAULT_MAX_STORAGE_SIZE, + bool security_enabled = false); bzn::raft_state get_state() override; @@ -77,6 +78,8 @@ namespace bzn bzn::message get_status() override; + bool get_security_enabled() override { return this->security_enabled; }; + private: friend class raft_log_base; friend class raft_log; @@ -160,6 +163,8 @@ namespace bzn void send_session_error_message(std::shared_ptr session, const std::string& error_message); + bool validate_new_peer(std::shared_ptr session, const bzn::message &peer); + // raft state... bzn::raft_state current_state = raft_state::follower; uint32_t current_term = 0; @@ -197,6 +202,6 @@ namespace bzn bool enable_audit = true; - bool whitelist_enabled{false}; // TODO: RHN - this is only temporary, until whitelist is tested and in use. + bool security_enabled{false}; // TODO: RHN - this is only temporary, until the security functionality is tested and in use. }; } // bzn diff --git a/raft/raft_base.hpp b/raft/raft_base.hpp index 3a7e30ea..95c18083 100644 --- a/raft/raft_base.hpp +++ b/raft/raft_base.hpp @@ -138,6 +138,16 @@ namespace bzn */ virtual void register_commit_handler(bzn::raft_base::commit_handler handler) = 0; + + /** + * Returns the state of ht esecurity enabled flag. True if a peer added to the swarm via + * the add_peer ws command will be validated against the Bluzelle Private Key and node + * blacklist. + * + * @return boolean, state of security validation + */ + virtual bool get_security_enabled() = 0; + }; diff --git a/raft/raft_log.hpp b/raft/raft_log.hpp index 3026ca21..a496103d 100644 --- a/raft/raft_log.hpp +++ b/raft/raft_log.hpp @@ -21,8 +21,7 @@ #include -namespace bzn -{ +namespace bzn { const std::string MSG_ERROR_EMPTY_LOG_ENTRY_FILE{"Empty log entry file. Please delete .state folder."}; const std::string MSG_NO_PEERS_IN_LOG{"Unable to find peers in log entries."}; @@ -30,14 +29,15 @@ namespace bzn const std::string MSG_UNABLE_TO_CREATE_LOG_PATH_NAMED{"Unable to create log path: "}; const std::string MSG_EXITING_DUE_TO_LOG_PATH_CREATION_FAILURE{"MSG_EXITING_DUE_TO_LOG_PATH_CREATION_FAILURE"}; const std::string MSG_ERROR_MAXIMUM_STORAGE_EXCEEDED{"Maximum storage has been exceeded, please update the options file."}; + const size_t DEFAULT_MAX_STORAGE_SIZE{2147483648}; class raft_log { public: // The default maximum allowed storage for a node is 2G - static const size_t DEFAULT_MAX_STORAGE_SIZE = 2147483648; + //const size_t DEFAULT_MAX_STORAGE_SIZE = 2147483648; - raft_log(const std::string& log_path, const size_t max_storage = bzn::raft_log::DEFAULT_MAX_STORAGE_SIZE); + raft_log(const std::string& log_path, const size_t max_storage = bzn::DEFAULT_MAX_STORAGE_SIZE); const bzn::log_entry& entry_at(size_t i) const; const bzn::log_entry& last_quorum_entry() const; diff --git a/raft/test/raft_test.cpp b/raft/test/raft_test.cpp index 3fb0520c..2821280a 100644 --- a/raft/test/raft_test.cpp +++ b/raft/test/raft_test.cpp @@ -298,7 +298,7 @@ namespace bzn } std::shared_ptr - start_raft(const bzn::peers_list_t& peer_list, bzn::asio::wait_handler& asio_wait_handler, bzn::message_handler bzn_msg_handler) + start_raft(const bzn::peers_list_t& peer_list, bzn::asio::wait_handler& asio_wait_handler, bzn::message_handler bzn_msg_handler, bool security_enabled = false) { auto mock_steady_timer = std::make_unique>(); @@ -321,7 +321,9 @@ namespace bzn , this->mock_node , peer_list , TEST_NODE_UUID - , TEST_STATE_DIR); + , TEST_STATE_DIR + , bzn::DEFAULT_MAX_STORAGE_SIZE + , security_enabled); //bzn::message_handler mh; EXPECT_CALL(*mock_node, register_for_message("raft", _)).WillOnce(Invoke( @@ -1193,7 +1195,6 @@ namespace bzn } - TEST_F(raft_test, test_that_bad_add_or_remove_peer_requests_fail) { auto mock_steady_timer = std::make_unique>(); @@ -1563,12 +1564,22 @@ namespace bzn auto raft = bzn::raft(std::make_shared>(), nullptr, TEST_PEER_LIST, TEST_NODE_UUID, TEST_STATE_DIR); auto peers = raft.get_all_peers(); + auto mock_session = std::make_shared(); + EXPECT_EQ(TEST_PEER_LIST.size(), peers.size()); EXPECT_EQ(TEST_PEER_LIST,peers); + EXPECT_CALL(*mock_session, send_message(An>(),_)) + .WillOnce(Invoke( + [&](const auto& msg, auto) + { + auto root = *msg.get(); + EXPECT_EQ(root["response"]["msg"].asString(), SUCCESS_PEER_ADDED_TO_SWARM); + })); + // replace the last quorum with a joint quorum raft.current_state = bzn::raft_state::leader; - raft.handle_ws_raft_messages(make_add_peer_request(), nullptr); + raft.handle_ws_raft_messages(make_add_peer_request(), mock_session); const auto& active = raft.get_active_quorum(); EXPECT_EQ(active.size(), size_t(2)); @@ -1609,6 +1620,17 @@ namespace bzn clean_state_folder(); auto raft = bzn::raft(std::make_shared>(), nullptr, TEST_PEER_LIST, TEST_NODE_UUID, TEST_STATE_DIR); + auto mock_session = std::make_shared(); + + EXPECT_CALL(*mock_session, send_message(An>(),_)) + .WillOnce(Invoke( + [&](const auto& msg, auto) + { + auto root = *msg.get(); + EXPECT_EQ(root["response"]["msg"].asString(), SUCCESS_PEER_ADDED_TO_SWARM); + })); + + EXPECT_EQ(bzn::log_entry_type::single_quorum, raft.raft_log->last_quorum_entry().entry_type); auto active_list = raft.get_active_quorum(); @@ -1636,7 +1658,7 @@ namespace bzn // replace the last quorum with a joint quorum bzn::message add_peer = make_add_peer_request(); raft.current_state = bzn::raft_state::leader; - raft.handle_ws_raft_messages(add_peer, nullptr); + raft.handle_ws_raft_messages(add_peer, mock_session); active_list = raft.get_active_quorum(); EXPECT_EQ(active_list.size(), size_t(2)); @@ -1666,6 +1688,17 @@ namespace bzn clean_state_folder(); auto raft = bzn::raft(std::make_shared>(), nullptr, TEST_PEER_LIST, TEST_NODE_UUID, TEST_STATE_DIR); + auto mock_session = std::make_shared(); + + EXPECT_CALL(*mock_session, send_message(An>(),_)) + .WillOnce(Invoke( + [&](const auto& msg, auto) + { + auto root = *msg.get(); + EXPECT_EQ(root["response"]["msg"].asString(), SUCCESS_PEER_ADDED_TO_SWARM); + })); + + EXPECT_EQ(bzn::log_entry_type::single_quorum, raft.raft_log->last_quorum_entry().entry_type); std::set yes_votes; @@ -1682,7 +1715,7 @@ namespace bzn EXPECT_TRUE(raft.is_majority(yes_votes)); raft.current_state = bzn::raft_state::leader; - raft.handle_ws_raft_messages(make_add_peer_request(), nullptr); + raft.handle_ws_raft_messages(make_add_peer_request(), mock_session); yes_votes.clear(); yes_votes.emplace(new_peer.uuid); @@ -1838,4 +1871,14 @@ namespace bzn EXPECT_EQ(raft->no_votes.size(),size_t(2)); EXPECT_EQ(raft->get_state(), bzn::raft_state::candidate); } + + + + TEST_F(raft_test, test_that_add_node_works_securely) + { + + + } + + } // bzn diff --git a/utils/crypto.hpp b/utils/crypto.hpp index 4d95be25..c29f73e6 100644 --- a/utils/crypto.hpp +++ b/utils/crypto.hpp @@ -29,7 +29,7 @@ namespace bzn::utils::crypto * @param in/out vector of unsigned chars that will contain the decoded data * @return integer value of 0 on success */ - int base64_decode( const std::string& base64_message, std::vector& decoded_message); + int base64_decode(const std::string& base64_message, std::vector& decoded_message); /** diff --git a/utils/test/utils_test.cpp b/utils/test/utils_test.cpp index 15ddaf0d..ab60db15 100644 --- a/utils/test/utils_test.cpp +++ b/utils/test/utils_test.cpp @@ -31,7 +31,7 @@ namespace // NOTE - not constant as the test shuffles the list before use to get five // random uuids. - std::vector blacklisted_uuids = { + std::vector blacklisted_uuids { "9dc2f619-2e77-49f7-9b20-5b55fd87ea44", "51bfd541-ab3e-4f02-93c7-8c3328daccfa", "f06ab617-7ccc-45fe-aee2-d4f5d175891b",