From 0f0df5391383246a438d133c6f324065e81f9929 Mon Sep 17 00:00:00 2001 From: chalith Date: Wed, 1 Nov 2023 18:27:05 +0530 Subject: [PATCH] Added allowed user pubkey list for hpsh --- examples/js_client/text-client.js | 5 +++- src/conf.cpp | 20 +++++++++++++++ src/conf.hpp | 9 ++++--- src/hpsh/hpsh.cpp | 11 +++++--- src/hpsh/hpsh.hpp | 1 + src/msg/bson/usrmsg_bson.cpp | 16 ++++++++++-- src/msg/bson/usrmsg_bson.hpp | 2 +- src/msg/json/usrmsg_json.cpp | 22 +++++++++++++--- src/msg/json/usrmsg_json.hpp | 2 +- src/msg/usrmsg_common.hpp | 3 +++ src/msg/usrmsg_parser.cpp | 6 ++--- src/msg/usrmsg_parser.hpp | 2 +- src/usr/usr.cpp | 42 +++++++++++++++++++++++++------ src/usr/usr.hpp | 3 +++ 14 files changed, 117 insertions(+), 27 deletions(-) diff --git a/examples/js_client/text-client.js b/examples/js_client/text-client.js index 36e4e16d..3692406b 100644 --- a/examples/js_client/text-client.js +++ b/examples/js_client/text-client.js @@ -126,7 +126,10 @@ async function main() { else if (inp.startsWith("hpsh ")) { hpc.submitHpshRequest(inp.substr(5)).then(id => { hpc.on(id, (reply) => { - console.log(reply); + if (reply.data) + console.log(reply.data); + else + console.error(reply.error); }) }); } diff --git a/src/conf.cpp b/src/conf.cpp index 227b62d8..34baa7c4 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -528,6 +528,20 @@ namespace conf std::cerr << "Invalid format for hpsh run as config (\"uid>0:gid>0\" expected).\n"; return -1; } + + jpath = "hpsh.users"; + cfg.hpsh.users.clear(); + for (auto &userpk : hpsh["users"].array_range()) + { + // Convert the public key hex of each node to binary and store it. + const std::string bin_pubkey = util::to_bin(userpk.as()); + if (bin_pubkey.empty()) + { + std::cerr << "Error decoding user pubkey list.\n"; + return -1; + } + cfg.hpsh.users.emplace(bin_pubkey); + } } catch (const std::exception &e) { @@ -653,6 +667,12 @@ namespace conf jsoncons::ojson hpsh_config; hpsh_config.insert_or_assign("enabled", cfg.hpsh.enabled); hpsh_config.insert_or_assign("run_as", cfg.hpsh.run_as.to_string()); + jsoncons::ojson users(jsoncons::json_array_arg); + for (const auto &userpk : cfg.hpsh.users) + { + users.push_back(util::to_hex(userpk)); + } + hpsh_config.insert_or_assign("users", users); d.insert_or_assign("hpsh", hpsh_config); } diff --git a/src/conf.hpp b/src/conf.hpp index b91764f4..31a5357d 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -210,8 +210,9 @@ namespace conf struct hpsh_config { - bool enabled = false; // Whether or not to enable hpsh. - ugid run_as; // The user/groups id to execute the hpsh as. + bool enabled = false; // Whether or not to enable hpsh. + ugid run_as; // The user/groups id to execute the hpsh as. + std::set users; // List of users who are allowed to perform hpsh (list of binary public keys). }; struct user_config @@ -327,11 +328,11 @@ namespace conf }; // Global contract context struct exposed to the application. - // Other modeuls will access context values via this. + // Other modules will access context values via this. extern contract_ctx ctx; // Global configuration struct exposed to the application. - // Other modeuls will access config values via this. + // Other modules will access config values via this. extern hp_config cfg; int init(); diff --git a/src/hpsh/hpsh.cpp b/src/hpsh/hpsh.cpp index 6d5d7d94..d3a9ea96 100644 --- a/src/hpsh/hpsh.cpp +++ b/src/hpsh/hpsh.cpp @@ -174,6 +174,12 @@ namespace hpsh if (ctx.is_shutting_down) return -1; + if (conf::cfg.hpsh.users.find(std::string(user_pubkey)) == conf::cfg.hpsh.users.end()) + { + LOG_ERROR << "This user is not allowed to perform hpsh operations."; + return -2; + } + // Send the hpsh request header. if (write(ctx.control_fds[1], HPSH_CTR_SH, 3) < 0) { @@ -248,6 +254,7 @@ namespace hpsh while (!ctx.is_shutting_down) { + // Iterate through received commands and check for outputs. if (ctx.commands.size() > 0) { std::scoped_lock lock(ctx.command_mutex); @@ -290,9 +297,7 @@ namespace hpsh { const usr::connected_user &user = user_itr->second; msg::usrmsg::usrmsg_parser parser(user.protocol); - std::vector msg; - parser.create_hpsh_response_container(msg, itr->id, response); - user.session.send(msg); + usr::send_hpsh_response(std::move(parser), user.session, itr->id, msg::usrmsg::STATUS_ACCEPTED, response); response.clear(); } } diff --git a/src/hpsh/hpsh.hpp b/src/hpsh/hpsh.hpp index c368e991..6c859897 100644 --- a/src/hpsh/hpsh.hpp +++ b/src/hpsh/hpsh.hpp @@ -4,6 +4,7 @@ #include "../pchheader.hpp" #include "../conf.hpp" #include "../usr/usr.hpp" +#include "../msg/usrmsg_common.hpp" namespace hpsh { diff --git a/src/msg/bson/usrmsg_bson.cpp b/src/msg/bson/usrmsg_bson.cpp index 2505e734..ac415cf6 100644 --- a/src/msg/bson/usrmsg_bson.cpp +++ b/src/msg/bson/usrmsg_bson.cpp @@ -168,11 +168,13 @@ namespace msg::usrmsg::bson * { * "type": "hpsh_response", * "reply_for": "", - * "content": + * "status": "", + * "content": "" + * "reason": "", * } * @param content The contract binary output content to be put in the message. */ - void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view content) + void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view status, std::string_view content, std::string_view reason) { jsoncons::bson::bson_bytes_encoder encoder(msg); encoder.begin_object(); @@ -180,8 +182,18 @@ namespace msg::usrmsg::bson encoder.string_value(msg::usrmsg::MSGTYPE_HPSH_RESPONSE); encoder.key(msg::usrmsg::FLD_REPLY_FOR); encoder.string_value(reply_for); + encoder.key(msg::usrmsg::FLD_STATUS); + encoder.string_value(status); encoder.key(msg::usrmsg::FLD_CONTENT); encoder.byte_string_value(content); + + // Reject reason is only included for rejected inputs. + if (!reason.empty()) + { + encoder.key(msg::usrmsg::FLD_REASON); + encoder.string_value(reason); + } + encoder.end_object(); encoder.flush(); } diff --git a/src/msg/bson/usrmsg_bson.hpp b/src/msg/bson/usrmsg_bson.hpp index cc06be4f..e485195b 100644 --- a/src/msg/bson/usrmsg_bson.hpp +++ b/src/msg/bson/usrmsg_bson.hpp @@ -17,7 +17,7 @@ namespace msg::usrmsg::bson void create_contract_input_status(std::vector &msg, std::string_view status, std::string_view reason, std::string_view input_hash, const uint64_t ledger_seq_no, const util::h32 &ledger_hash); - void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view content); + void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view status, std::string_view content, std::string_view reason); void create_contract_read_response_container(std::vector &msg, std::string_view reply_for, std::string_view content); diff --git a/src/msg/json/usrmsg_json.cpp b/src/msg/json/usrmsg_json.cpp index 58d271cb..04c919ce 100644 --- a/src/msg/json/usrmsg_json.cpp +++ b/src/msg/json/usrmsg_json.cpp @@ -272,7 +272,7 @@ namespace msg::usrmsg::json * { * "type": "contract_input_status", * "status": "", - * "reason": "", + * "reason": "", * "input_hash": "", * "ledger_seq_no": , * "ledger_hash": "" @@ -332,11 +332,13 @@ namespace msg::usrmsg::json * { * "type": "hpsh_response", * "reply_for": "", - * "content": "" + * "status": "", + * "content": "" + * "reason": "", * } * @param content The contract binary output content to be put in the message. */ - void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view content) + void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view status, std::string_view content, std::string_view reason) { msg.reserve(content.size() + 256); msg += "{\""; @@ -348,6 +350,10 @@ namespace msg::usrmsg::json msg += SEP_COLON; msg += reply_for; msg += SEP_COMMA; + msg += msg::usrmsg::FLD_STATUS; + msg += SEP_COLON; + msg += status; + msg += SEP_COMMA; msg += msg::usrmsg::FLD_CONTENT; msg += SEP_COLON_NOQUOTE; @@ -368,6 +374,16 @@ namespace msg::usrmsg::json msg += content; } + // Reject reason is only included for rejected inputs. + if (!reason.empty()) + { + msg += SEP_COMMA_NOQUOTE; + msg += msg::usrmsg::FLD_REASON; + msg += SEP_COLON; + msg += reason; + msg += DOUBLE_QUOTE; + } + msg += "}"; } diff --git a/src/msg/json/usrmsg_json.hpp b/src/msg/json/usrmsg_json.hpp index ff5c827d..0f4b2149 100644 --- a/src/msg/json/usrmsg_json.hpp +++ b/src/msg/json/usrmsg_json.hpp @@ -21,7 +21,7 @@ namespace msg::usrmsg::json void create_contract_input_status(std::vector &msg, std::string_view status, std::string_view reason, std::string_view input_hash, const uint64_t ledger_seq_no, const util::h32 &ledger_hash); - void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view content); + void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view status, std::string_view content, std::string_view reason); void create_contract_read_response_container(std::vector &msg, std::string_view reply_for, std::string_view content); diff --git a/src/msg/usrmsg_common.hpp b/src/msg/usrmsg_common.hpp index 6844e578..0e291495 100644 --- a/src/msg/usrmsg_common.hpp +++ b/src/msg/usrmsg_common.hpp @@ -108,6 +108,9 @@ namespace msg::usrmsg constexpr const char *REASON_NONCE_EXPIRED = "nonce_expired"; constexpr const char *REASON_ALREADY_SUBMITTED = "already_submitted"; constexpr const char *REASON_ROUND_INPUTS_OVERFLOW = "round_inputs_overflow"; + constexpr const char *REASON_USER_NOT_ALLOWED = "user_not_allowed"; + constexpr const char *REASON_NOT_INITIALIZED = "not_initialized"; + constexpr const char *REASON_INTERNAL_ERROR = "internal_error"; constexpr const char *QUERY_FILTER_BY_SEQ_NO = "seq_no"; constexpr const char *STR_TRUE = "true"; constexpr const char *STR_FALSE = "false"; diff --git a/src/msg/usrmsg_parser.cpp b/src/msg/usrmsg_parser.cpp index 69a3e4bd..52bb2952 100644 --- a/src/msg/usrmsg_parser.cpp +++ b/src/msg/usrmsg_parser.cpp @@ -38,12 +38,12 @@ namespace msg::usrmsg busrmsg::create_contract_input_status(msg, status, reason, input_hash, ledger_seq_no, ledger_hash); } - void usrmsg_parser::create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view content) const + void usrmsg_parser::create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view status, std::string_view content, std::string_view reason) const { if (protocol == util::PROTOCOL::JSON) - jusrmsg::create_hpsh_response_container(msg, reply_for, content); + jusrmsg::create_hpsh_response_container(msg, reply_for, status, content, reason); else - busrmsg::create_hpsh_response_container(msg, reply_for, content); + busrmsg::create_hpsh_response_container(msg, reply_for, status, content, reason); } void usrmsg_parser::create_contract_read_response_container(std::vector &msg, std::string_view reply_for, std::string_view content) const diff --git a/src/msg/usrmsg_parser.hpp b/src/msg/usrmsg_parser.hpp index 8ca9ce2d..ad06eec7 100644 --- a/src/msg/usrmsg_parser.hpp +++ b/src/msg/usrmsg_parser.hpp @@ -26,7 +26,7 @@ namespace msg::usrmsg void create_contract_input_status(std::vector &msg, std::string_view status, std::string_view reason, std::string_view input_hash, const uint64_t ledger_seq_no, const util::h32 &ledger_hash) const; - void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view content) const; + void create_hpsh_response_container(std::vector &msg, std::string_view reply_for, std::string_view status, std::string_view content, std::string_view reason) const; void create_contract_read_response_container(std::vector &msg, std::string_view reply_for, std::string_view content) const; diff --git a/src/usr/usr.cpp b/src/usr/usr.cpp index bb0839be..48393c38 100644 --- a/src/usr/usr.cpp +++ b/src/usr/usr.cpp @@ -276,18 +276,33 @@ namespace usr else if (msg_type == msg::usrmsg::MSGTYPE_HPSH_REQUEST) { std::string id, content; - if (hpsh::ctx.is_initialized && parser.extract_hpsh_request(id, content) != -1) - { - if (hpsh::execute(id, user.pubkey, content) == -1) - return -1; - - return 0; - } - else + if (parser.extract_hpsh_request(id, content) == -1) { send_input_status(parser, user.session, msg::usrmsg::STATUS_REJECTED, msg::usrmsg::REASON_BAD_MSG_FORMAT, ""); return -1; } + + // If hpsh is initialized, send status reject. + if (!hpsh::ctx.is_initialized) + { + send_hpsh_response(parser, user.session, id, msg::usrmsg::STATUS_REJECTED, "", msg::usrmsg::REASON_NOT_INITIALIZED); + return -1; + } + + const int res = hpsh::execute(id, user.pubkey, content); + // Send user npt allowed status if not allowed. + if (res == -1) + { + send_hpsh_response(parser, user.session, id, msg::usrmsg::STATUS_REJECTED, "", msg::usrmsg::REASON_INTERNAL_ERROR); + return -1; + } + else if (res == -2) + { + send_hpsh_response(parser, user.session, id, msg::usrmsg::STATUS_REJECTED, "", msg::usrmsg::REASON_USER_NOT_ALLOWED); + return -1; + } + + return 0; } else { @@ -350,6 +365,17 @@ namespace usr } } + /** + * Send the specified hpsh request status result via the provided session. + */ + void send_hpsh_response(const msg::usrmsg::usrmsg_parser &parser, usr::user_comm_session &session, std::string_view reply_for, + std::string_view status, std::string_view content, std::string_view reason) + { + std::vector msg; + parser.create_hpsh_response_container(msg, reply_for, status, content, reason); + session.send(msg); + } + /** * Send the specified contract input status result via the provided session. */ diff --git a/src/usr/usr.hpp b/src/usr/usr.hpp index 6e9ceef3..fa9753df 100644 --- a/src/usr/usr.hpp +++ b/src/usr/usr.hpp @@ -95,6 +95,9 @@ namespace usr void send_input_status_responses(const std::unordered_map> &responses, const uint64_t ledger_seq_no = 0, const util::h32 &ledger_hash = util::h32_empty); + void send_hpsh_response(const msg::usrmsg::usrmsg_parser &parser, usr::user_comm_session &session, std::string_view reply_for, + std::string_view status, std::string_view content, std::string_view reason = ""); + void send_input_status(const msg::usrmsg::usrmsg_parser &parser, usr::user_comm_session &session, std::string_view status, std::string_view reason, std::string_view input_hash, const uint64_t ledger_seq_no = 0, const util::h32 &ledger_hash = util::h32_empty);