libminifi/include/c2/C2Payload.h (204 lines of code) (raw):

/** * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <vector> #include <memory> #include <utility> #include <string> #include <map> #include <limits> #include "core/state/Value.h" #include "core/state/UpdateController.h" #include "utils/Enum.h" #include "utils/gsl.h" #include "utils/span.h" #include "rapidjson/document.h" namespace org::apache::nifi::minifi::c2 { enum class Operation : uint8_t { acknowledge, start, stop, restart, describe, heartbeat, update, clear, transfer, pause, resume, sync }; enum class DescribeOperand : uint8_t { metrics, configuration, manifest, jstack, corecomponentstate }; enum class UpdateOperand : uint8_t { configuration, properties, asset }; enum class TransferOperand : uint8_t { debug }; enum class ClearOperand : uint8_t{ connection, repositories, corecomponentstate }; enum class SyncOperand : uint8_t{ resource }; #define PAYLOAD_NO_STATUS 0 #define PAYLOAD_SUCCESS 1 #define PAYLOAD_FAILURE 2 enum Direction { TRANSMIT, RECEIVE }; class C2Value { public: friend std::ostream& operator<<(std::ostream& out, const C2Value& val); C2Value() = default; C2Value(const C2Value& other) { (*this) = other; } C2Value(C2Value&&) = default; template<typename T> requires(std::constructible_from<state::response::ValueNode, T>) explicit C2Value(T&& value) { value_ = state::response::ValueNode{std::forward<T>(value)}; } explicit C2Value(const rapidjson::Value& json_value) { value_.emplace<rapidjson::Document>(); get<rapidjson::Document>(value_).CopyFrom(json_value, get<rapidjson::Document>(value_).GetAllocator()); } explicit C2Value(rapidjson::Document&& json_doc) { value_ = std::move(json_doc); } C2Value& operator=(const C2Value& other) { if (auto* other_val_node = get_if<state::response::ValueNode>(&other.value_)) { value_ = *other_val_node; } else { value_.emplace<rapidjson::Document>(); get<rapidjson::Document>(value_).CopyFrom(get<rapidjson::Document>(other.value_), get<rapidjson::Document>(value_).GetAllocator()); } return *this; } C2Value& operator=(C2Value&&) = default; bool empty() const { if (auto* val_node = get_if<state::response::ValueNode>(&value_)) { return val_node->empty(); } return false; } std::string to_string() const { if (auto* val_node = get_if<state::response::ValueNode>(&value_)) { return val_node->to_string(); } return std::string{get<rapidjson::Document>(value_).GetString(), get<rapidjson::Document>(value_).GetStringLength()}; } const rapidjson::Document* json() const { return get_if<rapidjson::Document>(&value_); } const state::response::ValueNode* valueNode() const { return get_if<state::response::ValueNode>(&value_); } bool operator==(const C2Value&) const = default; private: std::variant<state::response::ValueNode, rapidjson::Document> value_; }; struct C2ContentResponse { explicit C2ContentResponse(Operation op) :op{ op } {} C2ContentResponse(const C2ContentResponse&) = default; C2ContentResponse(C2ContentResponse&&) = default; C2ContentResponse& operator=(const C2ContentResponse&) = default; C2ContentResponse& operator=(C2ContentResponse&&) = default; bool operator==(const C2ContentResponse &other) const { return std::tie(this->op, this->required, this->ident, this->name, this->operation_arguments) == std::tie(other.op, other.required, other.ident, other.name, other.operation_arguments); } bool operator!=(const C2ContentResponse &rhs) const { return !(*this == rhs); } friend std::ostream& operator<<(std::ostream& out, const C2ContentResponse& response); std::optional<std::string> getStringArgument(const std::string& arg_name) const { if (auto it = operation_arguments.find(arg_name); it != operation_arguments.end()) { return it->second.to_string(); } return std::nullopt; } Operation op; // determines if the operation is required bool required{ false }; // identifier std::string ident; // delay before running uint32_t delay{ 0 }; // max time before this response will no longer be honored. uint64_t ttl{ std::numeric_limits<uint64_t>::max() }; // name applied to commands std::string name; // commands that correspond with the operation. std::map<std::string, C2Value> operation_arguments; }; /** * C2Payload is an update for the state manager. * Note that the payload can either consist of other payloads or * have content directly within it, represented by C2ContentResponse objects, above. * * Payloads can also contain raw data, which can be binary data. */ class C2Payload : public state::Update { public: C2Payload(Operation op, std::string identifier, bool isRaw = false); C2Payload(Operation op, state::UpdateState state, std::string identifier, bool isRaw = false); explicit C2Payload(Operation op, bool isRaw = false); C2Payload(Operation op, state::UpdateState state, bool isRaw = false); C2Payload(const C2Payload&) = default; C2Payload(C2Payload&&) = default; C2Payload &operator=(const C2Payload&) = default; C2Payload &operator=(C2Payload&&) = default; ~C2Payload() override = default; void setIdentifier(std::string ident) { ident_ = std::move(ident); } [[nodiscard]] std::string getIdentifier() const { return ident_; } void setLabel(std::string label) { label_ = std::move(label); } [[nodiscard]] std::string getLabel() const { return label_; } /** * Gets the operation for this payload. May be nested or a single operation. */ [[nodiscard]] Operation getOperation() const noexcept { return op_; } /** * Validate the payload, if necessary and/or possible. */ bool validate() override { return true; } [[nodiscard]] const std::vector<C2ContentResponse> &getContent() const noexcept { return content_; } /** * Add a content response to this payload. */ void addContent(C2ContentResponse&&, bool collapsible = true); /** * Determines if this object contains raw data. */ [[nodiscard]] bool isRaw() const noexcept { return raw_; } /** * Sets raw data within this object. */ void setRawData(const std::string&); void setRawData(const std::vector<char>&); void setRawData(std::span<const std::byte>); /** * Returns raw data. */ [[nodiscard]] std::vector<std::byte> getRawData() const noexcept { return raw_data_; } [[nodiscard]] std::string getRawDataAsString() const { const auto raw_data = getRawData(); return utils::span_to<std::string>(utils::as_span<const char>(std::span(raw_data))); } [[nodiscard]] std::vector<std::byte> moveRawData() && {return std::move(raw_data_);} /** * Add a nested payload. * @param payload payload to move into this object. */ void addPayload(C2Payload &&payload); [[nodiscard]] bool isCollapsible() const noexcept { return is_collapsible_; } void setCollapsible(bool is_collapsible) noexcept { is_collapsible_ = is_collapsible; } [[nodiscard]] bool isContainer() const noexcept { return is_container_; } void setContainer(bool is_container) noexcept { is_container_ = is_container; } [[nodiscard]] const std::vector<C2Payload> &getNestedPayloads() const & noexcept { return payloads_; } std::vector<C2Payload>&& getNestedPayloads() && noexcept {return std::move(payloads_);} void reservePayloads(size_t new_capacity) { payloads_.reserve(new_capacity); } bool operator==(const C2Payload &other) const { return std::tie(this->op_, this->ident_, this->label_, this->payloads_, this->content_, this->raw_, this->raw_data_) == std::tie(other.op_, other.ident_, other.label_, other.payloads_, other.content_, other.raw_, other.raw_data_); } bool operator!=(const C2Payload &rhs) const { return !(*this == rhs); } friend std::ostream& operator<<(std::ostream& out, const C2Payload& payload); [[nodiscard]] std::string str() const { std::stringstream ss; ss << *this; return std::move(ss).str(); } protected: std::string ident_; // identifier for this payload. std::string label_; std::vector<C2Payload> payloads_; std::vector<C2ContentResponse> content_; Operation op_; bool raw_{ false }; std::vector<std::byte> raw_data_; bool is_container_{ false }; bool is_collapsible_{ true }; }; } // namespace org::apache::nifi::minifi::c2