blob: 729a62a30eacb6ce8d09a6f45a28e70351780609 [file] [log] [blame]
/**
* 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.
*/
#include <regex>
#include "unit/Catch.h"
#include "unit/ConfigurationTestController.h"
#include "catch2/generators/catch_generators.hpp"
#include "core/flow/FlowSchema.h"
#include "core/json/JsonFlowSerializer.h"
#include "core/json/JsonNode.h"
#include "utils/crypto/EncryptionProvider.h"
#include "utils/crypto/property_encryption/PropertyEncryptionUtils.h"
#include "utils/StringUtils.h"
#include "core/Resource.h"
#include "utils/Environment.h"
namespace org::apache::nifi::minifi::test {
constexpr std::string_view config_json_with_default_schema = R"({
"MiNiFi Config Version": 3,
"Flow Controller": {
"name": "MiNiFi Flow"
},
"Parameter Contexts": [
{
"id": "721e10b7-8e00-3188-9a27-476cca376978",
"name": "my-context",
"description": "my parameter context",
"Parameters": [
{
"name": "secret_parameter",
"description": "",
"sensitive": true,
"value": "param_value_1"
}
]
}
],
"Processors": [
{
"name": "Generate random data",
"id": "aabb6d26-8a8d-4338-92c9-1b8c67ec18e0",
"class": "org.apache.nifi.minifi.processors.GenerateFlowFile",
"scheduling strategy": "TIMER_DRIVEN",
"scheduling period": "15 sec",
"Properties": {
"File Size": "100B",
"Batch Size": "1",
"Unique FlowFiles": "true",
"Data Format": "Text"
}
},
{
"name": "Post files via secure HTTP",
"id": "469617f1-3898-4bbf-91fe-27d8f4dd2a75",
"class": "org.apache.nifi.minifi.processors.InvokeHTTP",
"scheduling strategy": "EVENT_DRIVEN",
"auto-terminated relationships list": ["success", "response", "failure", "retry", "no retry"],
"Properties": {
"Proxy Host": "https://my-proxy.com",
"Invalid HTTP Header Field Handling Strategy": "transform",
"Read Timeout": "15 s",
"invokehttp-proxy-password": "very_secure_password",
"Send Message Body": "true",
"Proxy Port": "12345",
"invokehttp-proxy-username": "user",
"Connection Timeout": "5 s",
"send-message-body": "true",
"Content-type": "application/octet-stream",
"SSL Context Service": "b9801278-7b5d-4314-aed6-713fd4b5f933",
"Always Output Response": "false",
"HTTP Method": "POST",
"Include Date Header": "true",
"Use Chunked Encoding": "false",
"Disable Peer Verification": "false",
"Penalize on \"No Retry\"": "false",
"Follow Redirects": "true",
"Remote URL": "https://my-storage-server.com/postdata"
}
}
],
"Connections": [
{
"name": "GenerateFlowFile/success/InvokeHTTP",
"id": "5c3d809b-0866-4c19-8287-439e5282c9c6",
"source id": "aabb6d26-8a8d-4338-92c9-1b8c67ec18e0",
"source relationship names": ["success"],
"destination id": "469617f1-3898-4bbf-91fe-27d8f4dd2a75"
}
],
"Controller Services": [
{
"name": "SSLContextService",
"id": "b9801278-7b5d-4314-aed6-713fd4b5f933",
"class": "org.apache.nifi.minifi.controllers.SSLContextService",
"Properties": {
"Private Key": "certs/agent-key.pem",
"Client Certificate": "certs/agent-cert.pem",
"Passphrase": "very_secure_passphrase",
"CA Certificate": "certs/ca-cert.pem",
"Use System Cert Store": "false"
}
}
],
"Remote Process Groups": []
}
)";
// in two parts because Visual Studio doesn't like long string constants
constexpr std::string_view config_json_with_nifi_schema_part_1 = R"({
"encodingVersion": {
"majorVersion": 2,
"minorVersion": 0
},
"maxTimerDrivenThreadCount": 1,
"maxEventDrivenThreadCount": 1,
"parameterContexts": [
{
"identifier": "721e10b7-8e00-3188-9a27-476cca376978",
"name": "my-context",
"description": "my parameter context",
"parameters": [
{
"name": "secret_parameter",
"description": "",
"sensitive": true,
"value": "param_value_1"
}
]
}
],
"rootGroup": {
"identifier": "8b4d66dc-9085-4722-b35b-3492f363baa3",
"instanceIdentifier": "0fab8422-3fbe-45e0-bd3a-324f5cb0592b",
"name": "root",
"position": {
"x": 0.0,
"y": 0.0
},
"processGroups": [],
"remoteProcessGroups": [],
"processors": [
{
"identifier": "aabb6d26-8a8d-4338-92c9-1b8c67ec18e0",
"instanceIdentifier": "ebbb1400-9fe6-4566-affd-1484a1cee58f",
"name": "GenerateFlowFile",
"comments": "",
"position": {
"x": 687.0,
"y": 372.0
},
"type": "org.apache.nifi.minifi.processors.GenerateFlowFile",
"bundle": {
"group": "org.apache.nifi.minifi",
"artifact": "minifi-standard-processors",
"version": "1.23.06"
},
"properties": {
"File Size": "100B",
"Batch Size": "1",
"Unique FlowFiles": "true",
"Data Format": "Text"
},
"propertyDescriptors": {
"File Size": {
"name": "File Size",
"identifiesControllerService": false,
"sensitive": false
},
"Batch Size": {
"name": "Batch Size",
"identifiesControllerService": false,
"sensitive": false
},
"Unique FlowFiles": {
"name": "Unique FlowFiles",
"identifiesControllerService": false,
"sensitive": false
},
"Custom Text": {
"name": "Custom Text",
"identifiesControllerService": false,
"sensitive": false
},
"Data Format": {
"name": "Data Format",
"identifiesControllerService": false,
"sensitive": false
}
},
"style": {},
"schedulingPeriod": "15 sec",
"schedulingStrategy": "TIMER_DRIVEN",
"executionNode": "ALL",
"penaltyDuration": "30000 ms",
"yieldDuration": "1000 ms",
"bulletinLevel": "WARN",
"runDurationMillis": 0,
"concurrentlySchedulableTaskCount": 1,
"autoTerminatedRelationships": [],
"componentType": "PROCESSOR",
"groupIdentifier": "8b4d66dc-9085-4722-b35b-3492f363baa3"
},
{
"identifier": "469617f1-3898-4bbf-91fe-27d8f4dd2a75",
"instanceIdentifier": "a43e3d65-bff9-4557-899b-43f680e4b533",
"name": "InvokeHTTP",
"comments": "",
"position": {
"x": 1031.0,
"y": 374.0
},
"type": "org.apache.nifi.minifi.processors.InvokeHTTP",
"bundle": {
"group": "org.apache.nifi.minifi",
"artifact": "minifi-standard-processors",
"version": "1.23.06"
},
"properties": {
"Proxy Host": "https://my-proxy.com",
"Invalid HTTP Header Field Handling Strategy": "transform",
"Read Timeout": "15 s",
"invokehttp-proxy-password": "very_secure_password",
"Send Message Body": "true",
"Proxy Port": "12345",
"invokehttp-proxy-username": "user",
"Connection Timeout": "5 s",
"send-message-body": "true",
"Content-type": "application/octet-stream",
"SSL Context Service": "b9801278-7b5d-4314-aed6-713fd4b5f933",
"Always Output Response": "false",
"HTTP Method": "POST",
"Include Date Header": "true",
"Use Chunked Encoding": "false",
"Disable Peer Verification": "false",
"Penalize on \"No Retry\"": "false",
"Follow Redirects": "true",
"Remote URL": "https://my-storage-server.com/postdata"
},
"propertyDescriptors": {
"Proxy Host": {
"name": "Proxy Host",
"identifiesControllerService": false,
"sensitive": false
},
"Attributes to Send": {
"name": "Attributes to Send",
"identifiesControllerService": false,
"sensitive": false
},
"Invalid HTTP Header Field Handling Strategy": {
"name": "Invalid HTTP Header Field Handling Strategy",
"identifiesControllerService": false,
"sensitive": false
},
"Read Timeout": {
"name": "Read Timeout",
"identifiesControllerService": false,
"sensitive": false
},
"invokehttp-proxy-password": {
"name": "invokehttp-proxy-password",
"displayName": "Proxy Password",
"identifiesControllerService": false,
"sensitive": true
},
"Send Message Body": {
"name": "Send Message Body",
"identifiesControllerService": false,
"sensitive": false
},
"Proxy Port": {
"name": "Proxy Port",
"identifiesControllerService": false,
"sensitive": false
},
"invokehttp-proxy-username": {
"name": "invokehttp-proxy-username",
"displayName": "Proxy Username",
"identifiesControllerService": false,
"sensitive": false
},
"Put Response Body in Attribute": {
"name": "Put Response Body in Attribute",
"identifiesControllerService": false,
"sensitive": false
},
"Connection Timeout": {
"name": "Connection Timeout",
"identifiesControllerService": false,
"sensitive": false
},
"send-message-body": {
"name": "send-message-body",
"displayName": "Send Body",
"identifiesControllerService": false,
"sensitive": false
},
"Content-type": {
"name": "Content-type",
"identifiesControllerService": false,
"sensitive": false
},
"SSL Context Service": {
"name": "SSL Context Service",
"identifiesControllerService": true,
"sensitive": false
},
"Always Output Response": {
"name": "Always Output Response",
"identifiesControllerService": false,
"sensitive": false
},
"HTTP Method": {
"name": "HTTP Method",
"identifiesControllerService": false,
"sensitive": false
},
"Include Date Header": {
"name": "Include Date Header",
"identifiesControllerService": false,
"sensitive": false
},
"Use Chunked Encoding": {
"name": "Use Chunked Encoding",
"identifiesControllerService": false,
"sensitive": false
},
"Disable Peer Verification": {
"name": "Disable Peer Verification",
"identifiesControllerService": false,
"sensitive": false
},
"Penalize on \"No Retry\"": {
"name": "Penalize on \"No Retry\"",
"identifiesControllerService": false,
"sensitive": false
},
"Follow Redirects": {
"name": "Follow Redirects",
"identifiesControllerService": false,
"sensitive": false
},
"Remote URL": {
"name": "Remote URL",
"identifiesControllerService": false,
"sensitive": false
}
},
"style": {},
"schedulingPeriod": "1000 ms",
"schedulingStrategy": "EVENT_DRIVEN",
"executionNode": "ALL",
"penaltyDuration": "30000 ms",
"yieldDuration": "1000 ms",
"bulletinLevel": "WARN",
"runDurationMillis": 0,
"concurrentlySchedulableTaskCount": 1,
"autoTerminatedRelationships": [
"success",
"response",
"failure",
"retry",
"no retry"
],
"componentType": "PROCESSOR",
"groupIdentifier": "8b4d66dc-9085-4722-b35b-3492f363baa3"
}
],
"inputPorts": [],
"outputPorts": [],
"connections": [
{
"identifier": "5c3d809b-0866-4c19-8287-439e5282c9c6",
"instanceIdentifier": "9d986de7-dd87-47ca-b3c6-46730c53f91d",
"name": "GenerateFlowFile/success/InvokeHTTP",
"position": {
"x": 0.0,
"y": 0.0
},
"source": {
"id": "aabb6d26-8a8d-4338-92c9-1b8c67ec18e0",
"type": "PROCESSOR",
"groupId": "8b4d66dc-9085-4722-b35b-3492f363baa3",
"name": "GenerateFlowFile",
"instanceIdentifier": "ebbb1400-9fe6-4566-affd-1484a1cee58f"
},
"destination": {
"id": "469617f1-3898-4bbf-91fe-27d8f4dd2a75",
"type": "PROCESSOR",
"groupId": "8b4d66dc-9085-4722-b35b-3492f363baa3",
"name": "InvokeHTTP",
"instanceIdentifier": "a43e3d65-bff9-4557-899b-43f680e4b533"
},
"labelIndex": 1,
"zIndex": 0,
"selectedRelationships": [
"success"
],
"backPressureObjectThreshold": 2000,
"backPressureDataSizeThreshold": "100 MB",
"flowFileExpiration": "0 seconds",
"prioritizers": [],
"bends": [],
"componentType": "CONNECTION",
"groupIdentifier": "8b4d66dc-9085-4722-b35b-3492f363baa3"
}
],
"labels": [],
"funnels": [],)";
constexpr std::string_view config_json_with_nifi_schema_part_2 = R"(
"controllerServices": [
{
"identifier": "b9801278-7b5d-4314-aed6-713fd4b5f933",
"instanceIdentifier": "61637b96-2cf7-4a9a-83b5-ea18fb205cd3",
"name": "SSLContextService",
"position": {
"x": 0.0,
"y": 0.0
},
"type": "org.apache.nifi.minifi.controllers.SSLContextService",
"bundle": {
"group": "org.apache.nifi.minifi",
"artifact": "minifi-system",
"version": "1.23.06"
},
"properties": {
"Private Key": "certs/agent-key.pem",
"Client Certificate": "certs/agent-cert.pem",
"Passphrase": "very_secure_passphrase",
"CA Certificate": "certs/ca-cert.pem",
"Use System Cert Store": "false"
},
"propertyDescriptors": {
"Private Key": {
"name": "Private Key",
"identifiesControllerService": false,
"sensitive": false
},
"Client Certificate": {
"name": "Client Certificate",
"identifiesControllerService": false,
"sensitive": false
},
"Passphrase": {
"name": "Passphrase",
"identifiesControllerService": false,
"sensitive": true
},
"CA Certificate": {
"name": "CA Certificate",
"identifiesControllerService": false,
"sensitive": false
},
"Use System Cert Store": {
"name": "Use System Cert Store",
"identifiesControllerService": false,
"sensitive": false
}
},
"controllerServiceApis": [
{
"type": "org.apache.nifi.minifi.controllers.SSLContextService",
"bundle": {
"group": "org.apache.nifi.minifi",
"artifact": "minifi-system",
"version": "1.23.06"
}
}
],
"componentType": "CONTROLLER_SERVICE",
"groupIdentifier": "8b4d66dc-9085-4722-b35b-3492f363baa3"
},
{
"identifier": "b418f4ff-e598-4ea2-921f-14f9dd864482",
"instanceIdentifier": "dbb76c00-97ad-4b3a-bf0c-d5d1b88e79d3",
"name": "Second SSLContextService",
"position": {
"x": 0.0,
"y": 0.0
},
"type": "org.apache.nifi.minifi.controllers.SSLContextService",
"bundle": {
"group": "org.apache.nifi.minifi",
"artifact": "minifi-system",
"version": "1.23.06"
},
"properties": {
"Private Key": "second_ssl_service_certs/agent-key.pem",
"Client Certificate": "second_ssl_service_certs/agent-cert.pem",
"CA Certificate": "second_ssl_service_certs/ca-cert.pem",
"Use System Cert Store": "false"
},
"propertyDescriptors": {
"Private Key": {
"name": "Private Key",
"identifiesControllerService": false,
"sensitive": false
},
"Client Certificate": {
"name": "Client Certificate",
"identifiesControllerService": false,
"sensitive": false
},
"Passphrase": {
"name": "Passphrase",
"identifiesControllerService": false,
"sensitive": true
},
"CA Certificate": {
"name": "CA Certificate",
"identifiesControllerService": false,
"sensitive": false
},
"Use System Cert Store": {
"name": "Use System Cert Store",
"identifiesControllerService": false,
"sensitive": false
}
},
"controllerServiceApis": [
{
"type": "org.apache.nifi.minifi.controllers.SSLContextService",
"bundle": {
"group": "org.apache.nifi.minifi",
"artifact": "minifi-system",
"version": "1.23.06"
}
}
],
"componentType": "CONTROLLER_SERVICE",
"groupIdentifier": "910ec043-2372-4edb-9ef4-8ce720c50685"
}
],
"variables": {},
"componentType": "PROCESS_GROUP"
}
}
)";
const minifi::utils::crypto::Bytes secret_key = minifi::utils::string::from_hex("75536923a75928a970077f9dae2c2b166a5413e020cb5190de4fb8edce1a38c7");
const minifi::utils::crypto::EncryptionProvider encryption_provider{secret_key};
using OverridesMap = std::unordered_map<minifi::utils::Identifier, core::flow::Overrides>;
TEST_CASE("JsonFlowSerializer can encrypt the sensitive properties") {
ConfigurationTestController test_controller;
core::flow::AdaptiveConfiguration json_configuration{test_controller.getContext()};
const auto [schema, flow_definition] = GENERATE(
std::make_tuple(core::flow::FlowSchema::getDefault(), std::string{config_json_with_default_schema}),
std::make_tuple(core::flow::FlowSchema::getNiFiFlowJson(), minifi::utils::string::join_pack(config_json_with_nifi_schema_part_1, config_json_with_nifi_schema_part_2)));
const auto process_group = json_configuration.getRootFromPayload(flow_definition);
REQUIRE(process_group);
rapidjson::Document doc;
rapidjson::ParseResult res = doc.Parse(flow_definition.data(), flow_definition.size());
REQUIRE(res);
const auto flow_serializer = core::json::JsonFlowSerializer{std::move(doc)};
const auto processor_id = minifi::utils::Identifier::parse("469617f1-3898-4bbf-91fe-27d8f4dd2a75").value();
const auto controller_service_id = minifi::utils::Identifier::parse("b9801278-7b5d-4314-aed6-713fd4b5f933").value();
const auto parameter_id = minifi::utils::Identifier::parse("721e10b7-8e00-3188-9a27-476cca376978").value();
const auto [overrides, expected_results] = GENERATE_REF(
std::make_tuple(OverridesMap{},
std::array{"very_secure_password", "very_secure_passphrase", "param_value_1"}),
std::make_tuple(OverridesMap{{processor_id, core::flow::Overrides{}.add("invokehttp-proxy-password", "password123")}},
std::array{"password123", "very_secure_passphrase", "param_value_1"}),
std::make_tuple(OverridesMap{{controller_service_id, core::flow::Overrides{}.add("Passphrase", "speak friend and enter")}},
std::array{"very_secure_password", "speak friend and enter", "param_value_1"}),
std::make_tuple(OverridesMap{{processor_id, core::flow::Overrides{}.add("invokehttp-proxy-password", "password123")},
{controller_service_id, core::flow::Overrides{}.add("Passphrase", "speak friend and enter")}},
std::array{"password123", "speak friend and enter", "param_value_1"}),
std::make_tuple(OverridesMap{{parameter_id, core::flow::Overrides{}.add("secret_parameter", "param_value_2")}},
std::array{"very_secure_password", "very_secure_passphrase", "param_value_2"}));
std::string config_json_encrypted = flow_serializer.serialize(*process_group, schema, encryption_provider, overrides, {});
{
std::regex regex{R"_("invokehttp-proxy-password": "(.*)",)_"};
std::smatch match_results;
CHECK(std::regex_search(config_json_encrypted, match_results, regex));
REQUIRE(match_results.size() == 2);
std::string encrypted_value = match_results[1];
CHECK(minifi::utils::crypto::property_encryption::decrypt(encrypted_value, encryption_provider) == expected_results[0]);
}
{
std::regex regex{R"_("Passphrase": "(.*)",)_"};
std::smatch match_results;
CHECK(std::regex_search(config_json_encrypted, match_results, regex));
REQUIRE(match_results.size() == 2);
std::string encrypted_value = match_results[1];
CHECK(minifi::utils::crypto::property_encryption::decrypt(encrypted_value, encryption_provider) == expected_results[1]);
}
{
std::regex regex{R"_("value": "(.*)")_"};
std::smatch match_results;
CHECK(std::regex_search(config_json_encrypted, match_results, regex));
REQUIRE(match_results.size() == 2);
std::string encrypted_value = match_results[1];
CHECK(minifi::utils::crypto::property_encryption::decrypt(encrypted_value, encryption_provider) == expected_results[2]);
}
}
TEST_CASE("JsonFlowSerializer with an override can add a new property to the flow config file") {
ConfigurationTestController test_controller;
core::flow::AdaptiveConfiguration json_configuration{test_controller.getContext()};
const auto schema = core::flow::FlowSchema::getNiFiFlowJson();
const auto config_json_with_nifi_schema = minifi::utils::string::join_pack(config_json_with_nifi_schema_part_1, config_json_with_nifi_schema_part_2);
const auto process_group = json_configuration.getRootFromPayload(config_json_with_nifi_schema);
REQUIRE(process_group);
rapidjson::Document doc;
rapidjson::ParseResult res = doc.Parse(config_json_with_nifi_schema.data(), config_json_with_nifi_schema.size());
REQUIRE(res);
const auto flow_serializer = core::json::JsonFlowSerializer{std::move(doc)};
const auto second_controller_service_id = minifi::utils::Identifier::parse("b418f4ff-e598-4ea2-921f-14f9dd864482").value();
SECTION("with required overrides") {
const OverridesMap overrides{{second_controller_service_id, core::flow::Overrides{}.add("Passphrase", "new passphrase")}};
std::string config_json_encrypted = flow_serializer.serialize(*process_group, schema, encryption_provider, overrides, {});
std::regex regex{R"_("Passphrase": "(.*)")_"};
std::smatch match_results;
// skip the first match
REQUIRE(std::regex_search(config_json_encrypted.cbegin(), config_json_encrypted.cend(), match_results, regex));
// verify the second match
REQUIRE(std::regex_search(match_results.suffix().first, config_json_encrypted.cend(), match_results, regex));
REQUIRE(match_results.size() == 2);
std::string encrypted_value = match_results[1];
CHECK(minifi::utils::crypto::property_encryption::decrypt(encrypted_value, encryption_provider) == "new passphrase");
}
SECTION("with optional overrides: the override is only used if the property is already in the flow config") {
const auto first_controller_service_id = minifi::utils::Identifier::parse("b9801278-7b5d-4314-aed6-713fd4b5f933").value();
const OverridesMap overrides{{first_controller_service_id, core::flow::Overrides{}.addOptional("Passphrase", "first new passphrase")},
{second_controller_service_id, core::flow::Overrides{}.addOptional("Passphrase", "second new passphrase")}};
std::string config_json_encrypted = flow_serializer.serialize(*process_group, schema, encryption_provider, overrides, {});
std::regex regex{R"_("Passphrase": "(.*)")_"};
std::smatch match_results;
// verify the first match
REQUIRE(std::regex_search(config_json_encrypted.cbegin(), config_json_encrypted.cend(), match_results, regex));
REQUIRE(match_results.size() == 2);
std::string encrypted_value = match_results[1];
CHECK(minifi::utils::crypto::property_encryption::decrypt(encrypted_value, encryption_provider) == "first new passphrase");
// check that there is no second match
CHECK_FALSE(std::regex_search(match_results.suffix().first, config_json_encrypted.cend(), match_results, regex));
}
}
TEST_CASE("The encrypted flow configuration can be decrypted with the correct key") {
ConfigurationTestController test_controller;
auto configuration_context = test_controller.getContext();
configuration_context.sensitive_values_encryptor = encryption_provider;
core::flow::AdaptiveConfiguration json_configuration_before{configuration_context};
const auto schema = core::flow::FlowSchema::getNiFiFlowJson();
const auto config_json_with_nifi_schema = minifi::utils::string::join_pack(config_json_with_nifi_schema_part_1, config_json_with_nifi_schema_part_2);
const auto process_group_before = json_configuration_before.getRootFromPayload(config_json_with_nifi_schema);
REQUIRE(process_group_before);
rapidjson::Document doc;
rapidjson::ParseResult res = doc.Parse(config_json_with_nifi_schema.data(), config_json_with_nifi_schema.size());
REQUIRE(res);
const auto flow_serializer = core::json::JsonFlowSerializer{std::move(doc)};
std::string config_json_encrypted = flow_serializer.serialize(*process_group_before, schema, encryption_provider, {}, {});
core::flow::AdaptiveConfiguration json_configuration_after{configuration_context};
const auto process_group_after = json_configuration_after.getRootFromPayload(config_json_encrypted);
REQUIRE(process_group_after);
const auto processor_id = minifi::utils::Identifier::parse("469617f1-3898-4bbf-91fe-27d8f4dd2a75").value();
const auto* processor_before = process_group_before->findProcessorById(processor_id);
REQUIRE(processor_before);
const auto* processor_after = process_group_after->findProcessorById(processor_id);
REQUIRE(processor_after);
CHECK(processor_before->getProperty("HTTP Method") == processor_after->getProperty("HTTP Method"));
CHECK(processor_before->getProperty("invokehttp-proxy-password") == processor_after->getProperty("invokehttp-proxy-password"));
const auto controller_service_id = "b9801278-7b5d-4314-aed6-713fd4b5f933";
const auto* const controller_service_node_before = process_group_before->findControllerService(controller_service_id);
REQUIRE(controller_service_node_before);
const auto* const controller_service_before = controller_service_node_before->getControllerServiceImplementation();
REQUIRE(controller_service_node_before);
const auto* const controller_service_node_after = process_group_after->findControllerService(controller_service_id);
REQUIRE(controller_service_node_after);
const auto* const controller_service_after = controller_service_node_before->getControllerServiceImplementation();
REQUIRE(controller_service_after);
CHECK(controller_service_before->getProperty("CA Certificate") == controller_service_after->getProperty("CA Certificate"));
CHECK(controller_service_before->getProperty("Passphrase") == controller_service_after->getProperty("Passphrase"));
const auto& param_contexts = json_configuration_after.getParameterContexts();
CHECK(param_contexts.at("my-context")->getParameter("secret_parameter")->value == "param_value_1");
}
TEST_CASE("The encrypted flow configuration cannot be decrypted with an incorrect key") {
ConfigurationTestController test_controller;
auto configuration_context = test_controller.getContext();
configuration_context.sensitive_values_encryptor = encryption_provider;
core::flow::AdaptiveConfiguration json_configuration_before{configuration_context};
const auto schema = core::flow::FlowSchema::getNiFiFlowJson();
const auto config_json_with_nifi_schema = minifi::utils::string::join_pack(config_json_with_nifi_schema_part_1, config_json_with_nifi_schema_part_2);
const auto process_group_before = json_configuration_before.getRootFromPayload(config_json_with_nifi_schema);
REQUIRE(process_group_before);
rapidjson::Document doc;
rapidjson::ParseResult res = doc.Parse(config_json_with_nifi_schema.data(), config_json_with_nifi_schema.size());
REQUIRE(res);
const auto flow_serializer = core::json::JsonFlowSerializer{std::move(doc)};
std::string config_json_encrypted = flow_serializer.serialize(*process_group_before, schema, encryption_provider, {}, {});
const minifi::utils::crypto::Bytes different_secret_key = minifi::utils::string::from_hex("ea55b7d0edc22280c9547e4d89712b3fae74f96d82f240a004fb9fbd0640eec7");
configuration_context.sensitive_values_encryptor = minifi::utils::crypto::EncryptionProvider{different_secret_key};
core::flow::AdaptiveConfiguration json_configuration_after{configuration_context};
REQUIRE_THROWS_AS(json_configuration_after.getRootFromPayload(config_json_encrypted), minifi::utils::crypto::EncryptionError);
}
TEST_CASE("Parameter provider generated parameter context is serialized correctly") {
ConfigurationTestController test_controller;
auto configuration_context = test_controller.getContext();
configuration_context.sensitive_values_encryptor = encryption_provider;
core::flow::AdaptiveConfiguration json_configuration_before{configuration_context};
const auto schema = core::flow::FlowSchema::getNiFiFlowJson();
static const std::string config_json_with_nifi_schema =
R"(
{
"parameterProviders": [
{
"identifier": "d26ee5f5-0192-1000-0482-4e333725e089",
"name": "EnvironmentVariableParameterProvider",
"type": "EnvironmentVariableParameterProvider",
"properties": {
"Environment Variable Inclusion Strategy": "Comma-Separated",
"Include Environment Variables": "MINIFI_DATA,SECRET_MINIFI_DATA",
"Sensitive Parameter Scope": "selected",
"Sensitive Parameter List": "SECRET_MINIFI_DATA",
"Parameter Group Name": "environment-variable-parameter-context"
}
}
],
"rootGroup": {
"name": "MiNiFi Flow",
"processors": [{
"name": "DummyProcessor",
"identifier": "aabb6d26-8a8d-4338-92c9-1b8c67ec18e0",
"type": "DummyProcessor",
"scheduling strategy": "TIMER_DRIVEN",
"scheduling period": "15 sec",
"properties": {
"Simple Property": "#{MINIFI_DATA}",
"Sensitive Property": "#{SECRET_MINIFI_DATA}"
}
}],
"parameterContextName": "environment-variable-parameter-context"
}
})";
minifi::utils::Environment::setEnvironmentVariable("MINIFI_DATA", "minifi_data_value");
minifi::utils::Environment::setEnvironmentVariable("SECRET_MINIFI_DATA", "secret_minifi_data_value");
const auto process_group_before = json_configuration_before.getRootFromPayload(config_json_with_nifi_schema);
REQUIRE(process_group_before);
std::string reserialized_config = json_configuration_before.serialize(*process_group_before);
rapidjson::Document result_doc;
rapidjson::ParseResult res = result_doc.Parse(reserialized_config.data(), reserialized_config.size());
REQUIRE(res);
REQUIRE(result_doc.HasMember("parameterContexts"));
auto parameters = result_doc["parameterContexts"].GetArray()[0]["parameters"].GetArray();
REQUIRE(parameters.Size() == 2);
for (const auto& parameter : parameters) {
std::string name = parameter["name"].GetString();
std::string value = parameter["value"].GetString();
if (name == "MINIFI_DATA") {
REQUIRE(value == "minifi_data_value");
} else if (name == "SECRET_MINIFI_DATA") {
REQUIRE(minifi::utils::crypto::property_encryption::decrypt(value, encryption_provider) == "secret_minifi_data_value");
}
}
}
TEST_CASE("Parameter provider generated parameter context is not serialized if parameter context already exists") {
ConfigurationTestController test_controller;
auto configuration_context = test_controller.getContext();
configuration_context.sensitive_values_encryptor = encryption_provider;
core::flow::AdaptiveConfiguration json_configuration_before{configuration_context};
const auto schema = core::flow::FlowSchema::getNiFiFlowJson();
static const std::string config_json_with_nifi_schema =
R"(
{
"parameterProviders": [
{
"identifier": "d26ee5f5-0192-1000-0482-4e333725e089",
"name": "EnvironmentVariableParameterProvider",
"type": "EnvironmentVariableParameterProvider",
"properties": {
"Environment Variable Inclusion Strategy": "Comma-Separated",
"Include Environment Variables": "MINIFI_DATA,SECRET_MINIFI_DATA",
"Sensitive Parameter Scope": "selected",
"Sensitive Parameter List": "SECRET_MINIFI_DATA",
"Parameter Group Name": "environment-variable-parameter-context"
}
}
],
"parameterContexts": [
{
"identifier": "123ee5f5-0192-1000-0482-4e333725e345",
"name": "environment-variable-parameter-context",
"description": "my parameter context",
"parameters": [
{
"name": "SECRET_MINIFI_DATA",
"description": "",
"sensitive": true,
"provided": true,
"value": "old_secret_minifi_data_value"
},
{
"name": "MINIFI_DATA",
"description": "",
"sensitive": false,
"provided": true,
"value": "old_minifi_data_value"
}
],
"parameterProvider": "d26ee5f5-0192-1000-0482-4e333725e089"
}
],
"rootGroup": {
"name": "MiNiFi Flow",
"processors": [{
"name": "DummyProcessor",
"identifier": "aabb6d26-8a8d-4338-92c9-1b8c67ec18e0",
"type": "DummyProcessor",
"scheduling strategy": "TIMER_DRIVEN",
"scheduling period": "15 sec",
"properties": {
"Simple Property": "#{MINIFI_DATA}",
"Sensitive Property": "#{SECRET_MINIFI_DATA}"
}
}],
"parameterContextName": "environment-variable-parameter-context"
}
})";
minifi::utils::Environment::setEnvironmentVariable("MINIFI_DATA", "minifi_data_value");
minifi::utils::Environment::setEnvironmentVariable("SECRET_MINIFI_DATA", "secret_minifi_data_value");
const auto process_group_before = json_configuration_before.getRootFromPayload(config_json_with_nifi_schema);
REQUIRE(process_group_before);
std::string reserialized_config = json_configuration_before.serialize(*process_group_before);
rapidjson::Document result_doc;
rapidjson::ParseResult res = result_doc.Parse(reserialized_config.data(), reserialized_config.size());
REQUIRE(res);
REQUIRE(result_doc.HasMember("parameterContexts"));
auto parameters = result_doc["parameterContexts"].GetArray()[0]["parameters"].GetArray();
REQUIRE(parameters.Size() == 2);
for (const auto& parameter : parameters) {
std::string name = parameter["name"].GetString();
std::string value = parameter["value"].GetString();
if (name == "MINIFI_DATA") {
REQUIRE(value == "old_minifi_data_value");
} else if (name == "SECRET_MINIFI_DATA") {
REQUIRE(minifi::utils::crypto::property_encryption::decrypt(value, encryption_provider) == "old_secret_minifi_data_value");
}
}
}
TEST_CASE("Parameter provider generated parameter context is reserialized if Reload Values On Restart is set to true") {
ConfigurationTestController test_controller;
auto configuration_context = test_controller.getContext();
configuration_context.sensitive_values_encryptor = encryption_provider;
core::flow::AdaptiveConfiguration json_configuration_before{configuration_context};
const auto schema = core::flow::FlowSchema::getNiFiFlowJson();
static const std::string config_json_with_nifi_schema =
R"(
{
"parameterProviders": [
{
"identifier": "d26ee5f5-0192-1000-0482-4e333725e089",
"name": "EnvironmentVariableParameterProvider",
"type": "EnvironmentVariableParameterProvider",
"properties": {
"Environment Variable Inclusion Strategy": "Comma-Separated",
"Include Environment Variables": "MINIFI_DATA,SECRET_MINIFI_DATA",
"Sensitive Parameter Scope": "selected",
"Sensitive Parameter List": "SECRET_MINIFI_DATA",
"Parameter Group Name": "environment-variable-parameter-context",
"Reload Values On Restart": "true"
}
}
],
"parameterContexts": [
{
"identifier": "123ee5f5-0192-1000-0482-4e333725e345",
"name": "environment-variable-parameter-context",
"description": "my parameter context",
"parameters": [
{
"name": "SECRET_MINIFI_DATA",
"description": "",
"sensitive": true,
"provided": true,
"value": "old_secret_minifi_data_value"
},
{
"name": "MINIFI_DATA",
"description": "",
"sensitive": false,
"provided": true,
"value": "old_minifi_data_value"
}
],
"parameterProvider": "d26ee5f5-0192-1000-0482-4e333725e089"
}
],
"rootGroup": {
"name": "MiNiFi Flow",
"processors": [{
"name": "DummyProcessor",
"identifier": "aabb6d26-8a8d-4338-92c9-1b8c67ec18e0",
"type": "DummyProcessor",
"scheduling strategy": "TIMER_DRIVEN",
"scheduling period": "15 sec",
"properties": {
"Simple Property": "#{MINIFI_DATA}",
"Sensitive Property": "#{SECRET_MINIFI_DATA}"
}
}],
"parameterContextName": "environment-variable-parameter-context"
}
})";
minifi::utils::Environment::setEnvironmentVariable("MINIFI_DATA", "minifi_data_value");
minifi::utils::Environment::setEnvironmentVariable("SECRET_MINIFI_DATA", "secret_minifi_data_value");
const auto process_group_before = json_configuration_before.getRootFromPayload(config_json_with_nifi_schema);
REQUIRE(process_group_before);
std::string reserialized_config = json_configuration_before.serialize(*process_group_before);
rapidjson::Document result_doc;
rapidjson::ParseResult res = result_doc.Parse(reserialized_config.data(), reserialized_config.size());
REQUIRE(res);
REQUIRE(result_doc.HasMember("parameterContexts"));
auto parameters = result_doc["parameterContexts"].GetArray()[0]["parameters"].GetArray();
REQUIRE(parameters.Size() == 2);
for (const auto& parameter : parameters) {
std::string name = parameter["name"].GetString();
std::string value = parameter["value"].GetString();
if (name == "MINIFI_DATA") {
CHECK(value == "minifi_data_value");
} else if (name == "SECRET_MINIFI_DATA") {
CHECK(minifi::utils::crypto::property_encryption::decrypt(value, encryption_provider) == "secret_minifi_data_value");
}
}
}
} // namespace org::apache::nifi::minifi::test