| /** |
| * 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 "ConfigFile.h" |
| |
| #include <algorithm> |
| #include <fstream> |
| #include <utility> |
| |
| #include "utils/StringUtils.h" |
| |
| namespace { |
| constexpr std::array<const char*, 2> DEFAULT_SENSITIVE_PROPERTIES{"nifi.security.client.pass.phrase", |
| "nifi.rest.api.password"}; |
| constexpr const char* ADDITIONAL_SENSITIVE_PROPS_PROPERTY_NAME = "nifi.sensitive.props.additional.keys"; |
| } // namespace |
| |
| namespace org { |
| namespace apache { |
| namespace nifi { |
| namespace minifi { |
| namespace encrypt_config { |
| |
| ConfigLine::ConfigLine(std::string line) : line_(line) { |
| line = utils::StringUtils::trim(line); |
| if (line.empty() || line[0] == '#') { return; } |
| |
| size_t index_of_first_equals_sign = line.find('='); |
| if (index_of_first_equals_sign == std::string::npos) { return; } |
| |
| std::string key = utils::StringUtils::trim(line.substr(0, index_of_first_equals_sign)); |
| if (key.empty()) { return; } |
| |
| key_ = key; |
| value_ = utils::StringUtils::trim(line.substr(index_of_first_equals_sign + 1)); |
| } |
| |
| ConfigLine::ConfigLine(std::string key, std::string value) |
| : line_{utils::StringUtils::join_pack(key, "=", value)}, key_{std::move(key)}, value_{std::move(value)} { |
| } |
| |
| void ConfigLine::updateValue(const std::string& value) { |
| auto pos = line_.find('='); |
| if (pos != std::string::npos) { |
| line_.replace(pos + 1, std::string::npos, value); |
| value_ = value; |
| } else { |
| throw std::invalid_argument{"Cannot update value in config line: it does not contain an = sign!"}; |
| } |
| } |
| |
| ConfigFile::ConfigFile(std::istream& input_stream) { |
| std::string line; |
| while (std::getline(input_stream, line)) { |
| config_lines_.push_back(ConfigLine{line}); |
| } |
| } |
| |
| ConfigFile::Lines::const_iterator ConfigFile::findKey(const std::string& key) const { |
| return std::find_if(config_lines_.cbegin(), config_lines_.cend(), [&key](const ConfigLine& config_line) { |
| return config_line.getKey() == key; |
| }); |
| } |
| |
| ConfigFile::Lines::iterator ConfigFile::findKey(const std::string& key) { |
| return std::find_if(config_lines_.begin(), config_lines_.end(), [&key](const ConfigLine& config_line) { |
| return config_line.getKey() == key; |
| }); |
| } |
| |
| bool ConfigFile::hasValue(const std::string& key) const { |
| const auto it = findKey(key); |
| return (it != config_lines_.end()); |
| } |
| |
| utils::optional<std::string> ConfigFile::getValue(const std::string& key) const { |
| const auto it = findKey(key); |
| if (it != config_lines_.end()) { |
| return it->getValue(); |
| } else { |
| return utils::nullopt; |
| } |
| } |
| |
| void ConfigFile::update(const std::string& key, const std::string& value) { |
| auto it = findKey(key); |
| if (it != config_lines_.end()) { |
| it->updateValue(value); |
| } else { |
| throw std::invalid_argument{"Key " + key + " not found in the config file!"}; |
| } |
| } |
| |
| void ConfigFile::insertAfter(const std::string& after_key, const std::string& key, const std::string& value) { |
| auto it = findKey(after_key); |
| if (it != config_lines_.end()) { |
| ++it; |
| config_lines_.emplace(it, key, value); |
| } else { |
| throw std::invalid_argument{"Key " + after_key + " not found in the config file!"}; |
| } |
| } |
| |
| void ConfigFile::append(const std::string& key, const std::string& value) { |
| config_lines_.emplace_back(key, value); |
| } |
| |
| int ConfigFile::erase(const std::string& key) { |
| auto has_this_key = [&key](const ConfigLine& line) { return line.getKey() == key; }; |
| auto new_end = std::remove_if(config_lines_.begin(), config_lines_.end(), has_this_key); |
| auto num_removed = std::distance(new_end, config_lines_.end()); |
| config_lines_.erase(new_end, config_lines_.end()); |
| return gsl::narrow<int>(num_removed); |
| } |
| |
| void ConfigFile::writeTo(const std::string& file_path) const { |
| try { |
| std::ofstream file{file_path}; |
| file.exceptions(std::ios::failbit | std::ios::badbit); |
| |
| for (const auto& config_line : config_lines_) { |
| file << config_line.getLine() << '\n'; |
| } |
| } catch (const std::exception&) { |
| throw std::runtime_error{"Could not write to file " + file_path}; |
| } |
| } |
| |
| std::vector<std::string> ConfigFile::getSensitiveProperties() const { |
| std::vector<std::string> sensitive_properties(DEFAULT_SENSITIVE_PROPERTIES.begin(), DEFAULT_SENSITIVE_PROPERTIES.end()); |
| const utils::optional<std::string> additional_sensitive_props_list = getValue(ADDITIONAL_SENSITIVE_PROPS_PROPERTY_NAME); |
| if (additional_sensitive_props_list) { |
| std::vector<std::string> additional_sensitive_properties = utils::StringUtils::split(*additional_sensitive_props_list, ","); |
| sensitive_properties = mergeProperties(sensitive_properties, additional_sensitive_properties); |
| } |
| |
| const auto not_found = [this](const std::string& property_name) { return !hasValue(property_name); }; |
| const auto new_end = std::remove_if(sensitive_properties.begin(), sensitive_properties.end(), not_found); |
| sensitive_properties.erase(new_end, sensitive_properties.end()); |
| |
| return sensitive_properties; |
| } |
| |
| std::vector<std::string> ConfigFile::mergeProperties(std::vector<std::string> properties, |
| const std::vector<std::string>& additional_properties) { |
| for (const auto& property_name : additional_properties) { |
| std::string property_name_trimmed = utils::StringUtils::trim(property_name); |
| if (!property_name_trimmed.empty()) { |
| properties.push_back(std::move(property_name_trimmed)); |
| } |
| } |
| |
| std::sort(properties.begin(), properties.end()); |
| auto new_end = std::unique(properties.begin(), properties.end()); |
| properties.erase(new_end, properties.end()); |
| return properties; |
| } |
| |
| } // namespace encrypt_config |
| } // namespace minifi |
| } // namespace nifi |
| } // namespace apache |
| } // namespace org |