mirror of
https://github.com/openappsec/openappsec.git
synced 2025-09-29 19:24:26 +03:00
Aug_23_2023-Dev
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
@@ -36,6 +38,46 @@ void
|
||||
AgentDetails::init()
|
||||
{
|
||||
registerMachineType();
|
||||
loadAccessToken();
|
||||
Singleton::Consume<I_MainLoop>::by<AgentDetails>()->addRecurringRoutine(
|
||||
I_MainLoop::RoutineType::System,
|
||||
chrono::seconds(60),
|
||||
[this] () { loadAccessToken(); },
|
||||
"Load access token"
|
||||
);
|
||||
proxies = {
|
||||
{ProxyProtocol::HTTP, ProxyData()},
|
||||
{ProxyProtocol::HTTPS, ProxyData()}
|
||||
};
|
||||
|
||||
auto proxy_config = getProfileAgentSetting<string>("agent.config.message.proxy");
|
||||
if (proxy_config.ok()) {
|
||||
setProxy(*proxy_config);
|
||||
writeAgentDetails();
|
||||
}
|
||||
|
||||
registerConfigLoadCb(
|
||||
[&]()
|
||||
{
|
||||
auto proxy_config = getProfileAgentSetting<string>("agent.config.message.proxy");
|
||||
if (proxy_config.ok()) {
|
||||
is_proxy_configured_via_settings = true;
|
||||
setProxy(*proxy_config);
|
||||
writeAgentDetails();
|
||||
} else if (is_proxy_configured_via_settings) {
|
||||
is_proxy_configured_via_settings = false;
|
||||
setProxy(string(""));
|
||||
writeAgentDetails();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
auto load_env_proxy = loadProxy();
|
||||
if (!load_env_proxy.ok()) {
|
||||
dbgDebug(D_ORCHESTRATOR)
|
||||
<< "Could not initialize load proxy from environment, Error: "
|
||||
<< load_env_proxy.getErr();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -260,6 +302,36 @@ AgentDetails::getOrchestrationMode() const
|
||||
return orchestration_mode;
|
||||
}
|
||||
|
||||
string
|
||||
AgentDetails::getAccessToken() const
|
||||
{
|
||||
return access_token;
|
||||
}
|
||||
|
||||
void
|
||||
AgentDetails::loadAccessToken()
|
||||
{
|
||||
readAgentDetails();
|
||||
auto data_path = getConfigurationWithDefault<string>(
|
||||
getFilesystemPathConfig() + "/data/",
|
||||
"encryptor",
|
||||
"Data files directory"
|
||||
);
|
||||
ifstream token_file(data_path + session_token_file_name);
|
||||
if (!token_file.is_open()) {
|
||||
dbgWarning(D_ORCHESTRATOR) << "Failed to open session token file: " << data_path + session_token_file_name;
|
||||
return;
|
||||
}
|
||||
stringstream token_steam;
|
||||
token_steam << token_file.rdbuf();
|
||||
|
||||
auto new_token = token_steam.str();
|
||||
if (access_token != new_token) {
|
||||
access_token = new_token;
|
||||
dbgTrace(D_ORCHESTRATOR) << "Loaded the new token";
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<I_AgentDetails::MachineType>
|
||||
AgentDetails::getMachineTypeFromDmiTable()
|
||||
{
|
||||
@@ -300,3 +372,235 @@ AgentDetails::registerMachineType()
|
||||
);
|
||||
dbgInfo(D_ORCHESTRATOR) << "Setting machine type " << static_cast<int>(machine_type.unpack());
|
||||
}
|
||||
|
||||
string
|
||||
AgentDetails::convertProxyProtocolToString(ProxyProtocol proto) const
|
||||
{
|
||||
switch(proto) {
|
||||
case ProxyProtocol::HTTP: return "http";
|
||||
case ProxyProtocol::HTTPS: return "https";
|
||||
}
|
||||
dbgAssert(false) << "Unsupported Proxy Protocol " << static_cast<int>(proto);
|
||||
return "";
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
AgentDetails::verifyProxySyntax(
|
||||
const string &protocol,
|
||||
const string &auth,
|
||||
const string &domain,
|
||||
const string &port,
|
||||
const string &env_proxy)
|
||||
{
|
||||
stringstream verify_string;
|
||||
verify_string
|
||||
<< protocol
|
||||
<< "://"
|
||||
<< (!auth.empty() ? auth + string("@") : "")
|
||||
<< domain
|
||||
<< ":"
|
||||
<< port
|
||||
<< (env_proxy.back() == '/' ? "/" : "");
|
||||
|
||||
if (env_proxy.compare(verify_string.str()) != 0) {
|
||||
return genError(string("Provided proxy has the wrong syntax:" ) + env_proxy);
|
||||
}
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
AgentDetails::loadProxyType(const string &proxy_type)
|
||||
{
|
||||
readAgentDetails();
|
||||
auto proxy_config = getProxy();
|
||||
if (proxy_config.ok()) {
|
||||
if (proxy_config.unpack() == "none") {
|
||||
return Maybe<string>(string());
|
||||
}
|
||||
return proxy_config;
|
||||
}
|
||||
|
||||
#ifdef gaia
|
||||
I_ShellCmd *shell_cmd = Singleton::Consume<I_ShellCmd>::by<AgentDetails>();
|
||||
auto proxy_ip = shell_cmd->getExecOutput("dbget proxy:ip-address| tr -d '\n'");
|
||||
if (!proxy_ip.ok()) return proxy_ip;
|
||||
auto proxy_port = shell_cmd->getExecOutput("dbget proxy:port| tr -d '\n'");
|
||||
if (!proxy_port.ok()) return proxy_port;
|
||||
if (*proxy_port != "" && *proxy_ip != "") return ("http://" + *proxy_ip + ":" + *proxy_port);
|
||||
|
||||
const string umis_file_path(string(getenv("CPDIR")) + "/tmp/umis_objects.C");
|
||||
|
||||
{
|
||||
ifstream umis_file(umis_file_path.c_str());
|
||||
if (!umis_file.good()) return Maybe<string>(string());
|
||||
}
|
||||
|
||||
const string read_umis_cmd = "cat " + umis_file_path + " | grep -w \"";
|
||||
const string parse_value_command = "\" | awk -F \"[ \\t]+\" '{printf $NF}' | tr -d \"()\"";
|
||||
|
||||
auto use_proxy = shell_cmd->getExecOutput(read_umis_cmd + "use_proxy" + parse_value_command);
|
||||
if (!use_proxy.ok())
|
||||
return genError("Failed to read use_proxy from " + umis_file_path + ": " + use_proxy.getErr());
|
||||
|
||||
if (use_proxy.unpack() == "true") {
|
||||
auto umis_proxy_add = shell_cmd->getExecOutput(read_umis_cmd + "proxy_address" + parse_value_command);
|
||||
if (!umis_proxy_add.ok() || *umis_proxy_add == "") return umis_proxy_add;
|
||||
auto umis_proxy_port = shell_cmd->getExecOutput(read_umis_cmd + "proxy_port" + parse_value_command);
|
||||
if (!umis_proxy_port.ok() || *umis_proxy_port == "") return umis_proxy_port;
|
||||
|
||||
return ("http://" + *umis_proxy_add + ":" + *umis_proxy_port);
|
||||
} else {
|
||||
dbgTrace(D_ORCHESTRATOR) << "Smart Console Proxy is turned off";
|
||||
}
|
||||
return Maybe<string>(string());
|
||||
#else // not gaia
|
||||
char *proxy = getenv(proxy_type.c_str());
|
||||
if (proxy) return string(proxy);
|
||||
|
||||
proxy = getenv(boost::algorithm::to_upper_copy(proxy_type).c_str());
|
||||
if (proxy) return string(proxy);
|
||||
return Maybe<string>(string());
|
||||
#endif // gaia
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
AgentDetails::loadProxyType(ProxyProtocol protocol)
|
||||
{
|
||||
dbgAssert(protocol == ProxyProtocol::HTTP || protocol == ProxyProtocol::HTTPS)
|
||||
<< "Unsupported Proxy Protocol " << static_cast<int>(protocol);
|
||||
|
||||
static const map<ProxyProtocol, string> env_var_name = {
|
||||
{ProxyProtocol::HTTPS, "https_proxy"},
|
||||
{ProxyProtocol::HTTP, "http_proxy"}
|
||||
};
|
||||
auto env_proxy = loadProxyType(env_var_name.at(protocol));
|
||||
if (!env_proxy.ok()) return genError(env_proxy.getErr());
|
||||
if (env_proxy.unpack().empty()) {
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
string protocol_regex = "(http|https)://";
|
||||
const static boost::regex no_auth_proxy_regex(protocol_regex + "(.)*:[0-9]{0,5}(/|)");
|
||||
const static boost::regex auth_proxy_regex(protocol_regex + "(.)*:(.)*@(.)*:[0-9]{0,5}(/|)");
|
||||
|
||||
ProxyData env_proxy_data;
|
||||
env_proxy_data.is_exists = true;
|
||||
string proxy_copy;
|
||||
if (!NGEN::Regex::regexMatch(__FILE__, __LINE__, env_proxy.unpack(), boost::regex(protocol_regex + "(.)*"))) {
|
||||
env_proxy = "http://" + env_proxy.unpack();
|
||||
}
|
||||
proxy_copy.assign(env_proxy.unpack());
|
||||
env_proxy_data.protocol = env_proxy.unpack().substr(0, proxy_copy.find(":"));
|
||||
proxy_copy.erase(0, proxy_copy.find(":") + 3); //remove "http://" or "https://"
|
||||
|
||||
if (NGEN::Regex::regexMatch(__FILE__, __LINE__, env_proxy.unpack(), auth_proxy_regex)) {
|
||||
env_proxy_data.auth = string(&proxy_copy[0], &proxy_copy[proxy_copy.find("@")]);
|
||||
proxy_copy.erase(0, proxy_copy.find("@") + 1); // remove "user:pass@"
|
||||
} else if (!NGEN::Regex::regexMatch(__FILE__, __LINE__, env_proxy.unpack(), no_auth_proxy_regex)) {
|
||||
return genError(string("Provided proxy has wrong syntax: ") + env_proxy.unpack());
|
||||
}
|
||||
env_proxy_data.domain = proxy_copy.substr(0, proxy_copy.find(":"));
|
||||
proxy_copy.erase(0, proxy_copy.find(":") + 1); // remove "host:"
|
||||
env_proxy_data.port = static_cast<uint16_t>(stoi(proxy_copy));
|
||||
|
||||
auto proxy_syntax = verifyProxySyntax(
|
||||
env_proxy_data.protocol,
|
||||
env_proxy_data.auth,
|
||||
env_proxy_data.domain,
|
||||
to_string(env_proxy_data.port),
|
||||
env_proxy.unpack()
|
||||
);
|
||||
if (!proxy_syntax.ok()) return proxy_syntax;
|
||||
if (env_proxy_data == proxies.at(protocol)) {
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
proxies.at(protocol) = env_proxy_data;
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< convertProxyProtocolToString(protocol)
|
||||
<< " proxy was successfully loaded, "
|
||||
<< getProxyAddress(protocol).unpack();
|
||||
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
AgentDetails::getProxyDomain(ProxyProtocol protocol) const
|
||||
{
|
||||
if (proxies.find(protocol) == proxies.end()) {
|
||||
return genError("Proxy type is not loaded in map, type: " + convertProxyProtocolToString(protocol));
|
||||
}
|
||||
if (proxies.at(protocol).domain.empty()) return genError(
|
||||
convertProxyProtocolToString(protocol) + string(" proxy domain is unset")
|
||||
);
|
||||
return proxies.at(protocol).domain;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
AgentDetails::getProxyCredentials(ProxyProtocol protocol) const
|
||||
{
|
||||
if (proxies.find(protocol) == proxies.end()) {
|
||||
return genError("Proxy type is not loaded in map, type: " + convertProxyProtocolToString(protocol));
|
||||
}
|
||||
if (proxies.at(protocol).auth.empty()) return genError(
|
||||
convertProxyProtocolToString(protocol) + string(" proxy auth is unset")
|
||||
);
|
||||
return proxies.at(protocol).auth;
|
||||
}
|
||||
|
||||
Maybe<uint16_t>
|
||||
AgentDetails::getProxyPort(ProxyProtocol protocol) const
|
||||
{
|
||||
if (proxies.find(protocol) == proxies.end()) {
|
||||
return genError("Proxy type is not loaded in map, type: " + convertProxyProtocolToString(protocol));
|
||||
}
|
||||
if (proxies.at(protocol).port == 0) return genError(
|
||||
convertProxyProtocolToString(protocol) + string(" proxy port is unset")
|
||||
);
|
||||
return proxies.at(protocol).port;
|
||||
}
|
||||
|
||||
bool
|
||||
AgentDetails::getProxyExists(ProxyProtocol protocol) const
|
||||
{
|
||||
if (proxies.find(protocol) == proxies.end()) {
|
||||
dbgInfo(D_ORCHESTRATOR)
|
||||
<< "Proxy type is not loaded in map, type: "
|
||||
<< convertProxyProtocolToString(protocol);
|
||||
return false;
|
||||
}
|
||||
return proxies.at(protocol).is_exists;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
AgentDetails::getProxyAddress(ProxyProtocol protocol) const
|
||||
{
|
||||
if (proxies.find(protocol) == proxies.end()) {
|
||||
return genError("Proxy type is not loaded in map, type: " + convertProxyProtocolToString(protocol));
|
||||
}
|
||||
if (proxies.at(protocol).protocol.empty() ||
|
||||
proxies.at(protocol).domain.empty() ||
|
||||
proxies.at(protocol).port == 0) {
|
||||
return genError(
|
||||
string("Can't construct ") +
|
||||
convertProxyProtocolToString(protocol) +
|
||||
string(" proxy address")
|
||||
);
|
||||
}
|
||||
return proxies.at(protocol).protocol +
|
||||
"://" +
|
||||
proxies.at(protocol).domain +
|
||||
":" +
|
||||
to_string(proxies.at(protocol).port);
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
AgentDetails::loadProxy()
|
||||
{
|
||||
if (getConfigurationFlag("orchestration-mode") == "offline_mode") return Maybe<void>();
|
||||
for (const auto &proxy_type : proxies) {
|
||||
auto loaded_proxy = loadProxyType(proxy_type.first);
|
||||
if (!loaded_proxy.ok()) return loaded_proxy;
|
||||
}
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
@@ -1,7 +1,11 @@
|
||||
#include "agent_details.h"
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "mock/mock_encryptor.h"
|
||||
#include "mock/mock_shell_cmd.h"
|
||||
#include "mock/mock_mainloop.h"
|
||||
#include "cptest.h"
|
||||
#include "config.h"
|
||||
#include "config_component.h"
|
||||
@@ -24,133 +28,6 @@ public:
|
||||
StrictMock<MockEncryptor> mock_encryptor;
|
||||
StrictMock<MockShellCmd> mock_shell_cmd;
|
||||
Config::I_Config *config = nullptr;
|
||||
StrictMock<MockMainLoop> mock_ml;
|
||||
};
|
||||
|
||||
TEST_F(AgentDetailsTest, doNothing)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(AgentDetailsTest, basicTest)
|
||||
{
|
||||
const vector<string> agent_details_vec {
|
||||
"{",
|
||||
" \"Fog domain\": \"fog.com\",",
|
||||
" \"Agent ID\": \"fdfdf-5454-dfd\",",
|
||||
" \"Fog port\": 443,",
|
||||
" \"Encrypted connection\": false,",
|
||||
" \"Orchestration mode\": \"offline_mode\",",
|
||||
" \"Tenant ID\": \"tenant_id\",",
|
||||
" \"Profile ID\": \"profile\",",
|
||||
" \"Proxy\": \"http://proxy.checkpoint.com/\",",
|
||||
" \"OpenSSL certificates directory\": \"\"",
|
||||
"}"
|
||||
};
|
||||
AgentDetails agent_details;
|
||||
env.preload();
|
||||
agent_details.preload();
|
||||
EXPECT_CALL(
|
||||
mock_shell_cmd,
|
||||
getExecOutput("dmidecode -s system-manufacturer | tr -d '\\n'", _, _)
|
||||
).WillOnce(Return(string("Microsoft Corporation")));
|
||||
env.init();
|
||||
agent_details.init();
|
||||
|
||||
auto i_conf = Singleton::Consume<Config::I_Config>::from(conf);
|
||||
i_conf->reloadConfiguration();
|
||||
|
||||
CPTestTempfile agent_details_file(agent_details_vec);
|
||||
setConfiguration(agent_details_file.fname, "Agent details", "File path");
|
||||
|
||||
EXPECT_TRUE(agent_details.readAgentDetails());
|
||||
EXPECT_EQ(agent_details.getFogDomain().unpack(), "fog.com");
|
||||
EXPECT_EQ(agent_details.getFogPort().unpack(), 443);
|
||||
EXPECT_EQ(agent_details.getAgentId(), "fdfdf-5454-dfd");
|
||||
EXPECT_FALSE(agent_details.getSSLFlag());
|
||||
|
||||
agent_details.setSSLFlag(true);
|
||||
agent_details.setFogPort(80);
|
||||
agent_details.setFogDomain("fog.checkpoint.com");
|
||||
agent_details.setAgentId("dfdfdf-dfd");
|
||||
agent_details.setClusterId("d5bd7949-554e-4fac-86c3-6e4e5d46a034");
|
||||
EXPECT_EQ(agent_details.getFogDomain().unpack(), "fog.checkpoint.com");
|
||||
EXPECT_EQ(agent_details.getFogPort().unpack(), 80);
|
||||
EXPECT_EQ(agent_details.getAgentId(), "dfdfdf-dfd");
|
||||
EXPECT_EQ(agent_details.getTenantId(), "tenant_id");
|
||||
EXPECT_EQ(agent_details.getProfileId(), "profile");
|
||||
EXPECT_EQ(agent_details.getClusterId(), "d5bd7949-554e-4fac-86c3-6e4e5d46a034");
|
||||
|
||||
EXPECT_TRUE(agent_details.writeAgentDetails());
|
||||
|
||||
EXPECT_TRUE(agent_details.readAgentDetails());
|
||||
EXPECT_EQ(agent_details.getFogDomain().unpack(), "fog.checkpoint.com");
|
||||
EXPECT_EQ(agent_details.getFogPort().unpack(), 80);
|
||||
EXPECT_EQ(agent_details.getAgentId(), "dfdfdf-dfd");
|
||||
EXPECT_EQ(agent_details.getClusterId(), "d5bd7949-554e-4fac-86c3-6e4e5d46a034");
|
||||
EXPECT_TRUE(agent_details.getSSLFlag());
|
||||
EXPECT_THAT(agent_details.getProxy(), IsValue("http://proxy.checkpoint.com/"));
|
||||
agent_details.setProxy("none");
|
||||
EXPECT_THAT(agent_details.getProxy(), IsValue("none"));
|
||||
|
||||
EXPECT_TRUE(agent_details.getOrchestrationMode() == OrchestrationMode::OFFLINE);
|
||||
agent_details.setOrchestrationMode(OrchestrationMode::ONLINE);
|
||||
EXPECT_TRUE(agent_details.getOrchestrationMode() == OrchestrationMode::ONLINE);
|
||||
auto machine_type = Singleton::Consume<I_Environment>::from(env)->get<I_AgentDetails::MachineType>("MachineType");
|
||||
EXPECT_EQ(machine_type.unpack(), I_AgentDetails::MachineType::AZURE);
|
||||
}
|
||||
|
||||
TEST_F(AgentDetailsTest, openSSL)
|
||||
{
|
||||
const vector<string> agent_details_vec {
|
||||
"{",
|
||||
" \"Fog domain\": \"fog.com\",",
|
||||
" \"Agent ID\": \"fdfdf-5454-dfd\",",
|
||||
" \"Fog port\": 443,",
|
||||
" \"Encrypted connection\": false,",
|
||||
" \"Tenant ID\": \"tenant_id\",",
|
||||
" \"Profile ID\": \"profile\",",
|
||||
" \"OpenSSL certificates directory\": \"\"",
|
||||
"}"
|
||||
};
|
||||
|
||||
AgentDetails agent_details;
|
||||
agent_details.preload();
|
||||
|
||||
CPTestTempfile agent_details_file(agent_details_vec);
|
||||
setConfiguration(agent_details_file.fname, "Agent details", "File path");
|
||||
|
||||
EXPECT_FALSE(agent_details.getSSLFlag());
|
||||
EXPECT_THAT(agent_details.getOpenSSLDir(), IsError("OpenSSL certificates directory was not set"));
|
||||
|
||||
agent_details.setOpenSSLDir("a/b/c");
|
||||
EXPECT_THAT(agent_details.getOpenSSLDir(), IsValue("a/b/c"));
|
||||
|
||||
agent_details.setFogPort(10);
|
||||
agent_details.setSSLFlag(false);
|
||||
agent_details.setFogDomain("www.fog.checkpoint.com");
|
||||
agent_details.setOpenSSLDir("");
|
||||
|
||||
EXPECT_THAT(agent_details.getFogPort(), IsValue(10));
|
||||
EXPECT_FALSE(agent_details.getSSLFlag());
|
||||
EXPECT_THAT(agent_details.getFogDomain(), IsValue("www.fog.checkpoint.com"));
|
||||
EXPECT_THAT(agent_details.getOpenSSLDir(), IsError("OpenSSL certificates directory was not set"));
|
||||
|
||||
EXPECT_FALSE(agent_details.getOrchestrationMode() == OrchestrationMode::OFFLINE);
|
||||
agent_details.setOrchestrationMode(OrchestrationMode::OFFLINE);
|
||||
EXPECT_TRUE(agent_details.getOrchestrationMode() == OrchestrationMode::OFFLINE);
|
||||
}
|
||||
|
||||
TEST_F(AgentDetailsTest, unrecognizedMachineType)
|
||||
{
|
||||
env.preload();
|
||||
env.init();
|
||||
AgentDetails agent_details;
|
||||
EXPECT_CALL(
|
||||
mock_shell_cmd,
|
||||
getExecOutput("dmidecode -s system-manufacturer | tr -d '\\n'", _, _)
|
||||
).WillOnce(Return(string("Skynet")));
|
||||
agent_details.preload();
|
||||
agent_details.init();
|
||||
|
||||
auto machine_type = Singleton::Consume<I_Environment>::from(env)->get<I_AgentDetails::MachineType>("MachineType");
|
||||
EXPECT_EQ(machine_type.unpack(), I_AgentDetails::MachineType::UNRECOGNIZED);
|
||||
}
|
||||
|
@@ -192,7 +192,7 @@ AgentDetailsReporter::Impl::addAttr(const map<string, string> &attr, bool allow_
|
||||
{
|
||||
dbgFlow(D_AGENT_DETAILS);
|
||||
bool ret = true;
|
||||
for (const pair<string, string> &single_attr : attr) {
|
||||
for (const auto &single_attr : attr) {
|
||||
if (!addAttr(single_attr.first, single_attr.second, allow_override)) ret = false;
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ AgentDetailsReporter::Impl::sendAttributes()
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const pair<string, string> &new_attr : new_attributes) {
|
||||
for (const auto &new_attr : new_attributes) {
|
||||
attributes[new_attr.first] = new_attr.second;
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ void
|
||||
AgentDetailsReporter::Impl::fini()
|
||||
{
|
||||
if (!new_attributes.empty()) {
|
||||
for (const pair<string, string> &new_attr : new_attributes) {
|
||||
for (const auto &new_attr : new_attributes) {
|
||||
attributes[new_attr.first] = new_attr.second;
|
||||
}
|
||||
}
|
||||
@@ -382,7 +382,7 @@ AgentDetailsReporter::Impl::sendReport(
|
||||
if (agent_version.ok()) additional_metadata.setAgentVersion(*agent_version);
|
||||
|
||||
if (!new_attributes.empty()) {
|
||||
for (const pair<string, string> &new_attr : new_attributes) {
|
||||
for (const auto &new_attr : new_attributes) {
|
||||
attributes[new_attr.first] = new_attr.second;
|
||||
}
|
||||
AttrSerializer<ofstream, cereal::JSONOutputArchive>(attributes, "save");
|
||||
|
@@ -823,7 +823,7 @@ ConfigComponent::Impl::fillMultiTenantExpectedConfigFiles(const map<string, set<
|
||||
auto global_path = getPolicyConfigPath(config_file.first, type);
|
||||
auto it = find(files.begin(), files.end(), global_path);
|
||||
if (it == files.end()) files.push_back(global_path);
|
||||
for (const pair<string, set<string>> &tenant_profiles : active_tenants) {
|
||||
for (const auto &tenant_profiles : active_tenants) {
|
||||
const string &tenant = tenant_profiles.first;
|
||||
const set<string> &profile_ids = tenant_profiles.second;
|
||||
for (const auto &profile_id : profile_ids) {
|
||||
|
@@ -12,6 +12,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <bitset>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
@@ -142,6 +143,130 @@ IPAddr::isInRange(const IPAddr &left, const IPAddr &right) const
|
||||
return (*this >= left) && (*this <= right);
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
IPAddr::calculateSubnetStart(int subnet_value)
|
||||
{
|
||||
if (type == IPType::V4) {
|
||||
return calculateSubnetStartV4(subnet_value);
|
||||
} else {
|
||||
return calculateSubnetStartV6(subnet_value);
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
IPAddr::calculateSubnetEnd(int subnet_value)
|
||||
{
|
||||
if (type == IPType::V4) {
|
||||
return calculateSubnetEndV4(subnet_value);
|
||||
} else {
|
||||
return calculateSubnetEndV6(subnet_value);
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
IPAddr::calculateSubnetStartV4(int subnet_value)
|
||||
{
|
||||
if (subnet_value < 0 || subnet_value > 32) {
|
||||
return genError("Invalid subnet value: ");
|
||||
}
|
||||
uint32_t ip = ntohl(v4.s_addr);
|
||||
uint32_t mask = (0xFFFFFFFF << (32 - subnet_value));
|
||||
uint32_t subnet = ip & mask;
|
||||
subnet = ntohl(subnet);
|
||||
return string(inet_ntoa(*(struct in_addr *)&subnet));
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
IPAddr::calculateSubnetStartV6(int subnet_value)
|
||||
{
|
||||
if (subnet_value < 0 || subnet_value > 128) {
|
||||
return genError("Invalid subnet value: ");
|
||||
}
|
||||
|
||||
// represent IPV6 as a binary
|
||||
bitset<128> mask;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
mask[i * 8 + j] = (v6.s6_addr[i] >> (7 - j)) & 1;
|
||||
}
|
||||
}
|
||||
|
||||
// set the subnet bits to 0
|
||||
for (int i = subnet_value; i < 128; i++) {
|
||||
mask.reset(i);
|
||||
}
|
||||
|
||||
// convert the binary to IPV6
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
uint8_t byteValue = 0;
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
byteValue |= (mask[i * 8 + j] << (7 - j));
|
||||
}
|
||||
v6.s6_addr[i] = byteValue;
|
||||
}
|
||||
|
||||
// convert to string
|
||||
ostringstream oss;
|
||||
for (int i = 0; i < 16; i+=2) {
|
||||
if (i > 0)
|
||||
oss << ":";
|
||||
oss << hex << ((v6.s6_addr[i] << 8) + v6.s6_addr[i+1]);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
IPAddr::calculateSubnetEndV4(int subnet_value)
|
||||
{
|
||||
if (subnet_value < 0 || subnet_value > 32) {
|
||||
return genError("Invalid subnet value: ");
|
||||
}
|
||||
uint32_t ip = ntohl(v4.s_addr);
|
||||
uint32_t mask = (0xFFFFFFFF << (32 - subnet_value));
|
||||
uint32_t subnet = ip & mask;
|
||||
subnet |= ~mask;
|
||||
subnet = ntohl(subnet);
|
||||
return string(inet_ntoa(*(struct in_addr *)&subnet));
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
IPAddr::calculateSubnetEndV6(int subnet_value)
|
||||
{
|
||||
if (subnet_value < 0 || subnet_value > 128) {
|
||||
return genError("Invalid subnet value: ");
|
||||
}
|
||||
|
||||
// represent IPV6 as a binary
|
||||
bitset<128> mask;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
mask[i * 8 + j] = (v6.s6_addr[i] >> (7 - j)) & 1;
|
||||
}
|
||||
}
|
||||
|
||||
// set the host bits to 1
|
||||
for (int i = subnet_value; i < 128; i++) {
|
||||
mask.set(i);
|
||||
}
|
||||
|
||||
// convert the binary to IPV6
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
uint8_t byteValue = 0;
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
byteValue |= (mask[i * 8 + j] << (7 - j));
|
||||
}
|
||||
v6.s6_addr[i] = byteValue;
|
||||
}
|
||||
|
||||
// convert to string
|
||||
ostringstream oss;
|
||||
for (int i = 0; i < 16; i += 2) {
|
||||
if (i > 0) oss << ":";
|
||||
oss << hex << ((v6.s6_addr[i] << 8) + v6.s6_addr[i + 1]);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
Maybe<IPAddr>
|
||||
IPAddr::createIPAddr(const string &ip_text)
|
||||
{
|
||||
|
@@ -544,7 +544,7 @@ Debug::applyOverrides()
|
||||
}
|
||||
} else {
|
||||
auto should_add_file_stream = true;
|
||||
for (const pair<string, shared_ptr<Debug::DebugStream>> &elem : active_streams) {
|
||||
for (const auto &elem : active_streams) {
|
||||
if (elem.first != "STDOUT" && elem.first != "FOG") should_add_file_stream = false;
|
||||
break;
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "cpnano_base64/base64.h"
|
||||
#include "config.h"
|
||||
|
@@ -22,8 +22,8 @@
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
#include "debug.h"
|
||||
#include "time_print.h"
|
||||
#include "debug.h"
|
||||
#include "singleton.h"
|
||||
#include "context.h"
|
||||
#include "table/table_helpers.h"
|
||||
|
@@ -25,8 +25,13 @@ public:
|
||||
:
|
||||
tenant_id(_tenant_id),
|
||||
profile_id(_profile_id)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
TenantProfilePair(const std::pair<std::string, std::string> &tenant_profile_pair)
|
||||
:
|
||||
tenant_id(tenant_profile_pair.first),
|
||||
profile_id(tenant_profile_pair.second)
|
||||
{}
|
||||
|
||||
size_t
|
||||
hash() const
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#include "i_rest_api.h"
|
||||
#include "i_messaging_buffer.h"
|
||||
#include "i_shell_cmd.h"
|
||||
#include "i_proxy_configuration.h"
|
||||
#include "component.h"
|
||||
|
||||
class ProtoMessageComp
|
||||
@@ -38,7 +39,8 @@ class ProtoMessageComp
|
||||
Singleton::Consume<I_Encryptor>,
|
||||
Singleton::Consume<I_Environment>,
|
||||
Singleton::Consume<I_MessagingBuffer>,
|
||||
Singleton::Consume<I_ShellCmd>
|
||||
Singleton::Consume<I_ShellCmd>,
|
||||
Singleton::Consume<I_ProxyConfiguration>
|
||||
{
|
||||
public:
|
||||
ProtoMessageComp();
|
||||
|
@@ -38,12 +38,14 @@ public:
|
||||
virtual std::string getProfileId() const = 0;
|
||||
|
||||
// Agent Details
|
||||
virtual Maybe<std::string> getProxy() const = 0;
|
||||
virtual void setProxy(const std::string &_proxy) = 0;
|
||||
virtual void setAgentId(const std::string &_agent_id) = 0;
|
||||
virtual std::string getAgentId() const = 0;
|
||||
virtual Maybe<std::string> getProxy() const = 0;
|
||||
virtual void setProxy(const std::string &_proxy) = 0;
|
||||
virtual void setAgentId(const std::string &_agent_id) = 0;
|
||||
virtual std::string getAgentId() const = 0;
|
||||
virtual void setOrchestrationMode(OrchestrationMode _orchstration_mode) = 0;
|
||||
virtual OrchestrationMode getOrchestrationMode() const = 0;
|
||||
virtual OrchestrationMode getOrchestrationMode() const = 0;
|
||||
virtual std::string getAccessToken() const = 0;
|
||||
virtual void loadAccessToken() = 0;
|
||||
|
||||
// OpenSSL
|
||||
virtual void setOpenSSLDir(const std::string &openssl_dir) = 0;
|
||||
|
@@ -31,12 +31,6 @@
|
||||
|
||||
USE_DEBUG_FLAG(D_COMMUNICATION);
|
||||
|
||||
enum class ProxyProtocol
|
||||
{
|
||||
HTTP,
|
||||
HTTPS
|
||||
};
|
||||
|
||||
enum class MessageTypeTag
|
||||
{
|
||||
GENERIC,
|
||||
@@ -141,16 +135,8 @@ public:
|
||||
return genError("Failed to download file. Error: " + response.getErr());
|
||||
}
|
||||
|
||||
virtual Maybe<std::string> getProxyDomain(ProxyProtocol protocol) const = 0;
|
||||
virtual Maybe<std::string> getProxyCredentials(ProxyProtocol protocol) const = 0;
|
||||
virtual Maybe<uint16_t> getProxyPort(ProxyProtocol protocol) const = 0;
|
||||
virtual bool getProxyExists(ProxyProtocol protocol) const = 0;
|
||||
virtual Maybe<std::string> getProxyAddress(ProxyProtocol protocol) const = 0;
|
||||
virtual Maybe<void> loadProxy() = 0;
|
||||
virtual bool setActiveFog(MessageTypeTag tag) = 0;
|
||||
virtual void loadAccessToken() = 0;
|
||||
virtual bool setActiveFog(const string &host, const uint16_t port, bool is_secure, MessageTypeTag tag) = 0;
|
||||
virtual std::string getAccessToken() = 0;
|
||||
|
||||
protected:
|
||||
~I_Messaging() {}
|
||||
|
38
core/include/services_sdk/interfaces/i_proxy_configuration.h
Normal file
38
core/include/services_sdk/interfaces/i_proxy_configuration.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (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.
|
||||
|
||||
#ifndef __I_PROXY_CONFIGURATION_H__
|
||||
#define __I_PROXY_CONFIGURATION_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "maybe_res.h"
|
||||
|
||||
enum class ProxyProtocol
|
||||
{
|
||||
HTTP,
|
||||
HTTPS
|
||||
};
|
||||
|
||||
class I_ProxyConfiguration
|
||||
{
|
||||
public:
|
||||
virtual Maybe<std::string> getProxyDomain(ProxyProtocol protocol) const = 0;
|
||||
virtual Maybe<std::string> getProxyCredentials(ProxyProtocol protocol) const = 0;
|
||||
virtual Maybe<uint16_t> getProxyPort(ProxyProtocol protocol) const = 0;
|
||||
virtual bool getProxyExists(ProxyProtocol protocol) const = 0;
|
||||
virtual Maybe<std::string> getProxyAddress(ProxyProtocol protocol) const = 0;
|
||||
virtual Maybe<void> loadProxy() = 0;
|
||||
};
|
||||
|
||||
#endif // __I_PROXY_CONFIGURATION_H__
|
@@ -26,6 +26,8 @@ public:
|
||||
MOCK_METHOD1(setProxy, void(const std::string&));
|
||||
MOCK_METHOD1(setAgentId, void(const std::string&));
|
||||
MOCK_CONST_METHOD0(getAgentId, std::string());
|
||||
MOCK_METHOD0(loadAccessToken, void());
|
||||
MOCK_CONST_METHOD0(getAccessToken, std::string());
|
||||
|
||||
// OpenSSL
|
||||
MOCK_METHOD1(setOpenSSLDir, void(const std::string&));
|
||||
|
@@ -57,20 +57,12 @@ public:
|
||||
)
|
||||
);
|
||||
|
||||
MOCK_METHOD0(loadAccessToken, void());
|
||||
MOCK_METHOD0(setActiveFog, bool());
|
||||
MOCK_METHOD1(setActiveFog, bool(MessageTypeTag));
|
||||
MOCK_METHOD0(unsetFogProxy, void());
|
||||
MOCK_METHOD0(loadFogProxy, void());
|
||||
MOCK_METHOD4(setActiveFog, bool(const string &, const uint16_t, const bool, MessageTypeTag));
|
||||
MOCK_METHOD0(getAccessToken, string());
|
||||
|
||||
MOCK_CONST_METHOD1(getProxyDomain, Maybe<std::string>(ProxyProtocol protocol));
|
||||
MOCK_CONST_METHOD1(getProxyCredentials, Maybe<std::string>(ProxyProtocol protocol));
|
||||
MOCK_CONST_METHOD1(getProxyPort, Maybe<uint16_t>(ProxyProtocol protocol));
|
||||
MOCK_CONST_METHOD1(getProxyExists, bool(ProxyProtocol protocol));
|
||||
MOCK_CONST_METHOD1(getProxyAddress, Maybe<std::string>(ProxyProtocol protocol));
|
||||
MOCK_METHOD0(loadProxy, Maybe<void>());
|
||||
};
|
||||
|
||||
#endif // __MOCK_MESSAGING_H__
|
||||
|
@@ -20,17 +20,42 @@
|
||||
#include "i_encryptor.h"
|
||||
#include "i_shell_cmd.h"
|
||||
#include "i_environment.h"
|
||||
#include "i_mainloop.h"
|
||||
#include "i_proxy_configuration.h"
|
||||
#include "singleton.h"
|
||||
#include "component.h"
|
||||
#include "enum_array.h"
|
||||
#include "agent_core_utilities.h"
|
||||
|
||||
class ProxyData
|
||||
{
|
||||
public:
|
||||
bool
|
||||
operator==(const ProxyData &other) const
|
||||
{
|
||||
return protocol==other.protocol &&
|
||||
domain==other.domain &&
|
||||
is_exists==other.is_exists &&
|
||||
port==other.port &&
|
||||
auth==other.auth;
|
||||
}
|
||||
|
||||
std::string protocol = "";
|
||||
std::string domain = "";
|
||||
std::string auth = "";
|
||||
bool is_exists = false;
|
||||
uint16_t port = 0;
|
||||
};
|
||||
|
||||
class AgentDetails
|
||||
:
|
||||
public Component,
|
||||
Singleton::Provide<I_AgentDetails>::SelfInterface,
|
||||
Singleton::Provide<I_ProxyConfiguration>::SelfInterface,
|
||||
Singleton::Consume<I_Encryptor>,
|
||||
Singleton::Consume<I_ShellCmd>,
|
||||
Singleton::Consume<I_Environment>
|
||||
Singleton::Consume<I_Environment>,
|
||||
Singleton::Consume<I_MainLoop>
|
||||
{
|
||||
public:
|
||||
AgentDetails() : Component("AgentDetails") {}
|
||||
@@ -39,15 +64,17 @@ public:
|
||||
|
||||
void init();
|
||||
|
||||
Maybe<std::string> getProxy() const;
|
||||
Maybe<std::string> getFogDomain() const;
|
||||
Maybe<uint16_t> getFogPort() const;
|
||||
std::string getAgentId() const;
|
||||
std::string getTenantId() const;
|
||||
std::string getProfileId() const;
|
||||
Maybe<std::string> getOpenSSLDir() const;
|
||||
std::string getClusterId() const;
|
||||
OrchestrationMode getOrchestrationMode() const;
|
||||
Maybe<std::string> getProxy() const;
|
||||
Maybe<std::string> getFogDomain() const;
|
||||
Maybe<uint16_t> getFogPort() const;
|
||||
std::string getAgentId() const;
|
||||
std::string getTenantId() const;
|
||||
std::string getProfileId() const;
|
||||
Maybe<std::string> getOpenSSLDir() const;
|
||||
std::string getClusterId() const;
|
||||
OrchestrationMode getOrchestrationMode() const;
|
||||
std::string getAccessToken() const;
|
||||
void loadAccessToken();
|
||||
|
||||
void setFogDomain(const std::string &_fog_domain) { fog_domain = _fog_domain; }
|
||||
void setFogPort(const uint16_t _fog_port) { fog_port = _fog_port; }
|
||||
@@ -67,6 +94,14 @@ public:
|
||||
void serialize(cereal::JSONInputArchive &ar);
|
||||
|
||||
void setClusterId(const std::string &_cluster_id);
|
||||
|
||||
Maybe<std::string> getProxyDomain(ProxyProtocol protocol) const;
|
||||
Maybe<std::string> getProxyCredentials(ProxyProtocol protocol) const;
|
||||
Maybe<uint16_t> getProxyPort(ProxyProtocol protocol) const;
|
||||
bool getProxyExists(ProxyProtocol protocol) const;
|
||||
Maybe<std::string> getProxyAddress(ProxyProtocol protocol) const;
|
||||
Maybe<void> loadProxy();
|
||||
|
||||
private:
|
||||
std::string fog_domain = "";
|
||||
std::string agent_id = "";
|
||||
@@ -77,13 +112,27 @@ private:
|
||||
std::string cluster_id = "";
|
||||
std::string filesystem_path = "/etc/cp";
|
||||
std::string log_files_path = "/var/log";
|
||||
std::string access_token = "";
|
||||
uint16_t fog_port = 0;
|
||||
bool encrypted_connection = false;
|
||||
OrchestrationMode orchestration_mode = OrchestrationMode::ONLINE;
|
||||
bool is_proxy_configured_via_settings = false;
|
||||
std::map<ProxyProtocol, ProxyData> proxies;
|
||||
|
||||
static const std::map<std::string, I_AgentDetails::MachineType> machineTypes;
|
||||
void registerMachineType();
|
||||
Maybe<I_AgentDetails::MachineType> getMachineTypeFromDmiTable();
|
||||
|
||||
std::string convertProxyProtocolToString(ProxyProtocol proto) const;
|
||||
Maybe<void> verifyProxySyntax(
|
||||
const std::string &protocol,
|
||||
const std::string &auth,
|
||||
const std::string &domain,
|
||||
const std::string &port,
|
||||
const std::string &env_proxy
|
||||
);
|
||||
Maybe<std::string> loadProxyType(const std::string &proxy_type);
|
||||
Maybe<void> loadProxyType(ProxyProtocol protocol);
|
||||
};
|
||||
|
||||
#endif // __AGENT_DETAILS_H__
|
||||
|
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "component.h"
|
||||
#include "time_proxy.h"
|
||||
#include "time_print.h"
|
||||
#include "debug.h"
|
||||
#include "config_component.h"
|
||||
#include "mainloop.h"
|
||||
|
@@ -90,10 +90,12 @@ DEFINE_FLAG(D_COMPONENT, D_ALL)
|
||||
DEFINE_FLAG(D_WAAP_PARSER_RAW, D_WAAP_PARSER)
|
||||
DEFINE_FLAG(D_WAAP_PARSER_URLENCODE, D_WAAP_PARSER)
|
||||
DEFINE_FLAG(D_WAAP_PARSER_PHPSERIALIZE, D_WAAP_PARSER)
|
||||
DEFINE_FLAG(D_WAAP_PARSER_PERCENT, D_WAAP_PARSER)
|
||||
DEFINE_FLAG(D_WAAP_OVERRIDE, D_WAAP)
|
||||
|
||||
DEFINE_FLAG(D_IPS, D_COMPONENT)
|
||||
DEFINE_FLAG(D_FILE_UPLOAD, D_COMPONENT)
|
||||
DEFINE_FLAG(D_RATE_LIMIT, D_COMPONENT)
|
||||
|
||||
DEFINE_FLAG(D_PARSER, D_COMPONENT)
|
||||
DEFINE_FLAG(D_WS, D_COMPONENT)
|
||||
|
@@ -28,7 +28,7 @@ namespace Intelligence
|
||||
{
|
||||
|
||||
enum class ClassifierType { CLASS, CATEGORY, FAMILY, GROUP, ORDER, KIND };
|
||||
enum class ObjectType { ASSET, ZONE, POLICY_PACKAGE, CONFIGURATION, SESSION };
|
||||
enum class ObjectType { ASSET, ZONE, POLICY_PACKAGE, CONFIGURATION, SESSION, SHORTLIVED };
|
||||
|
||||
class Invalidation
|
||||
{
|
||||
|
@@ -11,8 +11,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/// @file rest.h
|
||||
/// @brief Header file for RESTful communication functionalities.
|
||||
///
|
||||
/// This file defines classes and utilities for RESTful communication, including input/output handling,
|
||||
/// schema generation, and client-server interactions.
|
||||
|
||||
#ifndef __REST_H__
|
||||
#define __REST_H__
|
||||
|
||||
#include "cereal/archives/json.hpp"
|
||||
#include "cereal/types/common.hpp"
|
||||
#include "cereal/types/string.hpp"
|
||||
@@ -24,19 +31,32 @@
|
||||
|
||||
#include "debug.h"
|
||||
#include "maybe_res.h"
|
||||
|
||||
#include "rest/schema_printer.h"
|
||||
|
||||
/// @class JsonError
|
||||
/// @brief Class representing JSON parsing errors.
|
||||
///
|
||||
/// This class is used to represent errors that occur during JSON parsing.
|
||||
class JsonError
|
||||
{
|
||||
public:
|
||||
/// @brief Constructor for JsonError class.
|
||||
/// @param e The error message to be stored.
|
||||
JsonError(const std::string &e) : err(e) {}
|
||||
const std::string & getMsg() const { return err; }
|
||||
|
||||
/// @brief Retrieves the error message.
|
||||
/// @return The error message as a constant reference to a string.
|
||||
const std::string &getMsg() const { return err; }
|
||||
|
||||
private:
|
||||
std::string err;
|
||||
std::string err; ///< Error message.
|
||||
};
|
||||
|
||||
/// @class BasicRest
|
||||
/// @brief Base class for RESTful communication handling.
|
||||
///
|
||||
/// The BasicRest class provides basic functionalities for handling RESTful communication,
|
||||
/// including input/output handling, schema generation, and client-server interactions.
|
||||
class BasicRest
|
||||
{
|
||||
using InputFunc = std::function<void(cereal::JSONInputArchive &)>;
|
||||
@@ -44,16 +64,32 @@ class BasicRest
|
||||
using SchemaFunc = std::function<void(std::ostream &, int)>;
|
||||
|
||||
public:
|
||||
/// @brief Enumeration representing the direction of communication (Client to Server, Server to Client, or Both).
|
||||
enum class Direction { C2S, S2C, BOTH };
|
||||
|
||||
/// @brief Enumeration representing the type of parameter (Mandatory, Optional, or Default).
|
||||
enum class ParamType { MANDATORY, OPTIONAL, DEFAULT };
|
||||
|
||||
/// @brief Destructor for the BasicRest class.
|
||||
virtual ~BasicRest() = default;
|
||||
|
||||
void load(cereal::JSONInputArchive &ar) { for (auto it : input_funcs) it(ar); }
|
||||
/// @brief Loads data from the JSON input archive.
|
||||
/// @param ar The JSON input archive.
|
||||
void load(cereal::JSONInputArchive &ar) { for (auto it : input_funcs) it(ar); }
|
||||
|
||||
/// @brief Saves data to the JSON output archive.
|
||||
/// @param ar The JSON output archive.
|
||||
void save(cereal::JSONOutputArchive &ar) const { for (auto it : output_funcs) it(ar); }
|
||||
|
||||
/// @brief Outputs the schema to an output stream.
|
||||
/// @param out The output stream to write the schema to.
|
||||
/// @param level The indentation level for the schema.
|
||||
void performOutputingSchema(std::ostream &out, int level = 0);
|
||||
|
||||
/// @brief Adds a schema for the given REST parameter type.
|
||||
/// @tparam RestParamType The type of the REST parameter.
|
||||
/// @param label The label for the parameter in the schema.
|
||||
/// @param is_mandatory A boolean indicating whether the parameter is mandatory in the schema.
|
||||
template <typename RestParamType>
|
||||
void
|
||||
addSchema(const std::string &label, bool is_mandatory)
|
||||
@@ -67,6 +103,12 @@ public:
|
||||
if (is_mandatory) required.push_back(label);
|
||||
};
|
||||
|
||||
/// @brief Adds an input parameter of the given REST parameter type.
|
||||
/// @tparam RestParamType The type of the REST parameter.
|
||||
/// @param param The REST parameter to add as an input.
|
||||
/// @param label The label for the parameter in the input.
|
||||
/// @param type The parameter type (Mandatory, Optional, or Default).
|
||||
/// @param val The default value for the parameter (used for default parameters).
|
||||
template <typename RestParamType>
|
||||
void
|
||||
addInput(RestParam<RestParamType> ¶m, const std::string &label, ParamType type, const RestParamType &val)
|
||||
@@ -86,6 +128,12 @@ public:
|
||||
input_funcs.push_back(func);
|
||||
}
|
||||
|
||||
/// @brief Adds an output parameter of the given REST parameter type.
|
||||
/// @tparam RestParamType The type of the REST parameter.
|
||||
/// @param param The REST parameter to add as an output.
|
||||
/// @param label The label for the parameter in the output.
|
||||
/// @param type The parameter type (Mandatory, Optional, or Default).
|
||||
/// @param val The default value for the parameter (used for default parameters).
|
||||
template <typename RestParamType>
|
||||
void
|
||||
addOutput(RestParam<RestParamType> ¶m, const std::string &label, ParamType type, const RestParamType &val)
|
||||
@@ -111,37 +159,78 @@ private:
|
||||
void outputSchema(std::ostream &os, int level);
|
||||
void outputRequired(std::ostream &os, int level);
|
||||
|
||||
std::vector<InputFunc> input_funcs;
|
||||
std::vector<OutputFunc> output_funcs;
|
||||
std::vector<SchemaFunc> schema_func;
|
||||
std::vector<std::string> required;
|
||||
std::vector<InputFunc> input_funcs; ///< Vector storing input functions.
|
||||
std::vector<OutputFunc> output_funcs; ///< Vector storing output functions.
|
||||
std::vector<SchemaFunc> schema_func; ///< Vector storing schema functions.
|
||||
std::vector<std::string> required; ///< Vector storing the names of required parameters.
|
||||
};
|
||||
|
||||
/// @class ServerRest
|
||||
/// @brief Class representing a server-side RESTful communication handler.
|
||||
///
|
||||
/// The ServerRest class is used for server-side RESTful communication and provides
|
||||
/// functionality for handling REST calls.
|
||||
class ServerRest : public BasicRest
|
||||
{
|
||||
public:
|
||||
/// @brief Virtual function for handling a REST call.
|
||||
virtual void doCall() = 0;
|
||||
|
||||
/// @brief Performs the REST call using the input stream.
|
||||
/// @param in The input stream containing the JSON data for the REST call.
|
||||
/// @return A Maybe object containing the result of the REST call (either the JSON data or an error message).
|
||||
Maybe<std::string> performRestCall(std::istream &in);
|
||||
|
||||
protected:
|
||||
static constexpr bool isInput(BasicRest::Direction dir) { return dir != BasicRest::Direction::S2C; }
|
||||
/// @brief Determines if the direction is for input.
|
||||
/// @param dir The direction of the communication.
|
||||
/// @return True if the direction is for input, false otherwise.
|
||||
static constexpr bool isInput(BasicRest::Direction dir) { return dir != BasicRest::Direction::S2C; }
|
||||
|
||||
/// @brief Determines if the direction is for output.
|
||||
/// @param dir The direction of the communication.
|
||||
/// @return True if the direction is for output, false otherwise.
|
||||
static constexpr bool isOutput(BasicRest::Direction dir) { return dir != BasicRest::Direction::C2S; }
|
||||
|
||||
/// @brief Determines if the direction is for schema.
|
||||
/// @param dir The direction of the communication.
|
||||
/// @return True if the direction is for schema, false otherwise.
|
||||
static constexpr bool isSchema(BasicRest::Direction dir) { return dir != BasicRest::Direction::S2C; }
|
||||
};
|
||||
|
||||
/// @class ClientRest
|
||||
/// @brief Class representing a client-side RESTful communication handler.
|
||||
///
|
||||
/// The ClientRest class is used for client-side RESTful communication and provides
|
||||
/// functionality for generating and loading JSON data.
|
||||
class ClientRest : public BasicRest
|
||||
{
|
||||
public:
|
||||
/// @brief Generates JSON data from the object's state.
|
||||
/// @return A Maybe object containing the JSON data, or an error message if serialization fails.
|
||||
Maybe<std::string> genJson() const;
|
||||
|
||||
/// @brief Loads JSON data into the object's state.
|
||||
/// @param json The JSON data to be loaded.
|
||||
/// @return True if the JSON data is successfully loaded, false otherwise.
|
||||
bool loadJson(const std::string &json);
|
||||
|
||||
protected:
|
||||
static constexpr bool isInput(BasicRest::Direction dir) { return dir != BasicRest::Direction::C2S; }
|
||||
static constexpr bool isOutput(BasicRest::Direction dir) { return dir != BasicRest::Direction::S2C; }
|
||||
static constexpr bool isSchema(BasicRest::Direction) { return false; }
|
||||
};
|
||||
/// @brief Determines if the direction is for input.
|
||||
/// @param dir The direction of the communication.
|
||||
/// @return True if the direction is for input, false otherwise.
|
||||
static constexpr bool isInput(BasicRest::Direction dir) { return dir != BasicRest::Direction::C2S; }
|
||||
|
||||
/// @brief Determines if the direction is for output.
|
||||
/// @param dir The direction of the communication.
|
||||
/// @return True if the direction is for output, false otherwise.
|
||||
static constexpr bool isOutput(BasicRest::Direction dir) { return dir != BasicRest::Direction::S2C; }
|
||||
|
||||
/// @brief Determines if the direction is for schema.
|
||||
/// @param dir The direction of the communication.
|
||||
/// @return True if the direction is for schema, false otherwise.
|
||||
static constexpr bool isSchema(BasicRest::Direction) { return false; }
|
||||
};
|
||||
|
||||
template <bool is_input>
|
||||
class InputAdder
|
||||
@@ -303,34 +392,217 @@ private:
|
||||
std::map<std::string, N> obj;
|
||||
};
|
||||
|
||||
/// @def C2S_LABEL_PARAM(type, name, label)
|
||||
/// @brief Add a mandatory parameter for the Client-to-Server (C2S) direction.
|
||||
///
|
||||
/// This macro is used to add a mandatory parameter to a REST request sent from
|
||||
/// the client to the server.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
/// @param label The label or name of the parameter as it appears in the JSON data.
|
||||
#define C2S_LABEL_PARAM(type, name, label) \
|
||||
ADD_MANDATORY_PARAM(BasicRest::Direction::C2S, type, name, label)
|
||||
|
||||
/// @def S2C_LABEL_PARAM(type, name, label)
|
||||
/// @brief Add a mandatory parameter for the Server-to-Client (S2C) direction.
|
||||
///
|
||||
/// This macro is used to add a mandatory parameter to a REST response sent from
|
||||
/// the server to the client.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
/// @param label The label or name of the parameter as it appears in the JSON data.
|
||||
#define S2C_LABEL_PARAM(type, name, label) \
|
||||
ADD_MANDATORY_PARAM(BasicRest::Direction::S2C, type, name, label)
|
||||
|
||||
/// @def BOTH_LABEL_PARAM(type, name, label)
|
||||
/// @brief Add a mandatory parameter for both Client-to-Server (C2S) and Server-to-Client (S2C) directions.
|
||||
///
|
||||
/// This macro is used to add a mandatory parameter that is used in both the request
|
||||
/// and response of a REST communication.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
/// @param label The label or name of the parameter as it appears in the JSON data.
|
||||
#define BOTH_LABEL_PARAM(type, name, label) \
|
||||
ADD_MANDATORY_PARAM(BasicRest::Direction::BOTH, type, name, label)
|
||||
|
||||
/// @def C2S_PARAM(type, name)
|
||||
/// @brief Add a mandatory parameter for the Client-to-Server (C2S) direction with the parameter
|
||||
/// label being the same as the parameter name.
|
||||
///
|
||||
/// This macro is a shorthand for adding a mandatory parameter to a REST request with
|
||||
/// the same label as the parameter name.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
#define C2S_PARAM(type, name) C2S_LABEL_PARAM(type, name, #name)
|
||||
|
||||
/// @def S2C_PARAM(type, name)
|
||||
/// @brief Add a mandatory parameter for the Server-to-Client (S2C) direction with the parameter
|
||||
/// label being the same as the parameter name.
|
||||
///
|
||||
/// This macro is a shorthand for adding a mandatory parameter to a REST response with
|
||||
/// the same label as the parameter name.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
#define S2C_PARAM(type, name) S2C_LABEL_PARAM(type, name, #name)
|
||||
|
||||
/// @def BOTH_PARAM(type, name)
|
||||
/// @brief Add a mandatory parameter for both Client-to-Server (C2S) and Server-to-Client (S2C) directions
|
||||
/// with the parameter label being the same as the parameter name.
|
||||
///
|
||||
/// This macro is a shorthand for adding a mandatory parameter that is used in both the
|
||||
/// request and response of a REST communication with the same label as the parameter name.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
#define BOTH_PARAM(type, name) BOTH_LABEL_PARAM(type, name, #name)
|
||||
|
||||
/// @def C2S_LABEL_OPTIONAL_PARAM(type, name, label)
|
||||
/// @brief Add an optional parameter for the Client-to-Server (C2S) direction.
|
||||
///
|
||||
/// This macro is used to add an optional parameter to a REST request sent from the client
|
||||
/// to the server.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
/// @param label The label or name of the parameter as it appears in the JSON data.
|
||||
#define C2S_LABEL_OPTIONAL_PARAM(type, name, label) \
|
||||
ADD_OPTIONAL_PARAM(BasicRest::Direction::C2S, type, name, label)
|
||||
|
||||
/// @def S2C_LABEL_OPTIONAL_PARAM(type, name, label)
|
||||
/// @brief Add an optional parameter for the Server-to-Client (S2C) direction.
|
||||
///
|
||||
/// This macro is used to add an optional parameter to a REST response sent from the server
|
||||
/// to the client.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
/// @param label The label or name of the parameter as it appears in the JSON data.
|
||||
#define S2C_LABEL_OPTIONAL_PARAM(type, name, label) \
|
||||
ADD_OPTIONAL_PARAM(BasicRest::Direction::S2C, type, name, label)
|
||||
|
||||
/// @def BOTH_LABEL_OPTIONAL_PARAM(type, name, label)
|
||||
/// @brief Add an optional parameter for both Client-to-Server (C2S) and Server-to-Client (S2C) directions.
|
||||
///
|
||||
/// This macro is used to add an optional parameter that can be used in both the request
|
||||
/// and response of a REST communication.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
/// @param label The label or name of the parameter as it appears in the JSON data.
|
||||
#define BOTH_LABEL_OPTIONAL_PARAM(type, name, label) \
|
||||
ADD_OPTIONAL_PARAM(BasicRest::Direction::BOTH, type, name, label)
|
||||
|
||||
/// @def C2S_OPTIONAL_PARAM(type, name)
|
||||
/// @brief Add an optional parameter for the Client-to-Server (C2S) direction with the parameter label
|
||||
/// being the same as the parameter name.
|
||||
///
|
||||
/// This macro is a shorthand for adding an optional parameter to a REST request with
|
||||
/// the same label as the parameter name.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
#define C2S_OPTIONAL_PARAM(type, name) C2S_LABEL_OPTIONAL_PARAM(type, name, #name)
|
||||
|
||||
/// @def S2C_OPTIONAL_PARAM(type, name)
|
||||
/// @brief Add an optional parameter for the Server-to-Client (S2C) direction with the parameter label
|
||||
/// being the same as the parameter name.
|
||||
///
|
||||
/// This macro is a shorthand for adding an optional parameter to a REST response with
|
||||
/// the same label as the parameter name.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
#define S2C_OPTIONAL_PARAM(type, name) S2C_LABEL_OPTIONAL_PARAM(type, name, #name)
|
||||
|
||||
/// @def BOTH_OPTIONAL_PARAM(type, name)
|
||||
/// @brief Add an optional parameter for both Client-to-Server (C2S) and Server-to-Client (S2C) directions
|
||||
/// with the parameter label being the same as the parameter name.
|
||||
///
|
||||
/// This macro is a shorthand for adding an optional parameter that can be used in both the
|
||||
/// request and response of a REST communication with the same label as the parameter name.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
#define BOTH_OPTIONAL_PARAM(type, name) BOTH_LABEL_OPTIONAL_PARAM(type, name, #name)
|
||||
|
||||
/// @def C2S_LABEL_DEAFULT_PARAM(type, name, label, val)
|
||||
/// @brief Add a parameter with a default value for the Client-to-Server (C2S) direction.
|
||||
///
|
||||
/// This macro is used to add a parameter to a REST request with a default value. If the
|
||||
/// parameter is not provided in the request, the default value will be used.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
/// @param label The label or name of the parameter as it appears in the JSON data.
|
||||
/// @param val The default value of the parameter.
|
||||
#define C2S_LABEL_DEAFULT_PARAM(type, name, label, val) \
|
||||
ADD_DEFAULT_PARAM(BasicRest::Direction::C2S, type, name, label, val)
|
||||
|
||||
/// @def S2C_LABEL_DEAFULT_PARAM(type, name, label, val)
|
||||
/// @brief Add a parameter with a default value for the Server-to-Client (S2C) direction.
|
||||
///
|
||||
/// This macro is used to add a parameter to a REST response with a default value. If the
|
||||
/// parameter is not provided in the response, the default value will be used.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
/// @param label The label or name of the parameter as it appears in the JSON data.
|
||||
/// @param val The default value of the parameter.
|
||||
#define S2C_LABEL_DEAFULT_PARAM(type, name, label, val) \
|
||||
ADD_DEFAULT_PARAM(BasicRest::Direction::S2C, type, name, label, val)
|
||||
|
||||
/// @def BOTH_LABEL_DEAFULT_PARAM(type, name, label, val)
|
||||
/// @brief Add a parameter with a default value for both Client-to-Server (C2S) and Server-to-Client (S2C) directions.
|
||||
///
|
||||
/// This macro is used to add a parameter to a REST response with a default value. If the
|
||||
/// parameter is not provided in the response, the default value will be used.
|
||||
///
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
/// @param label The label or name of the parameter as it appears in the JSON data.
|
||||
/// @param val The default value of the parameter.
|
||||
#define BOTH_LABEL_DEAFULT_PARAM(type, name, label, val) \
|
||||
ADD_DEFAULT_PARAM(BasicRest::Direction::BOTH, type, name, label, val)
|
||||
|
||||
/// @def C2S_DEAFULT_PARAM(type, name, val)
|
||||
/// @brief Add a parameter with a default value for the Client-to-Server (C2S) direction with the parameter label
|
||||
/// being the same as the parameter name.
|
||||
///
|
||||
/// This macro is used to add a parameter to a REST request with
|
||||
/// the same label as the parameter name and a default value. If the
|
||||
/// parameter is not provided in the request, the default value will be used.
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
/// @param val The default value of the parameter.
|
||||
#define C2S_DEAFULT_PARAM(type, name, val) C2S_LABEL_DEAFULT_PARAM(type, name, #name, val)
|
||||
|
||||
/// @def C2S_DEAFULT_PARAM(type, name, val)
|
||||
/// @brief Add a parameter with a default value for the Server-to-Client (S2C) direction with the parameter
|
||||
/// label being the same as the parameter name.
|
||||
///
|
||||
/// This macro is used to add a parameter to a REST request with
|
||||
/// the same label as the parameter name and a default value. If the
|
||||
/// parameter is not provided in the request, the default value will be used.
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
/// @param val The default value of the parameter.
|
||||
#define S2C_DEAFULT_PARAM(type, name, val) S2C_LABEL_DEAFULT_PARAM(type, name, #name, val)
|
||||
|
||||
/// @def C2S_DEAFULT_PARAM(type, name, val)
|
||||
/// @brief Add a parameter with a default value for the Client-to-Server (C2S) and Server-to-Client (S2C) directions
|
||||
/// with the parameter label being the same as the parameter name.
|
||||
///
|
||||
/// This macro is used to add a parameter to a REST request with
|
||||
/// the same label as the parameter name and a default value. If the
|
||||
/// parameter is not provided in the request, the default value will be used.
|
||||
/// @param type The data type of the parameter.
|
||||
/// @param name The variable name of the parameter.
|
||||
/// @param val The default value of the parameter.
|
||||
#define BOTH_DEAFULT_PARAM(type, name, val) BOTH_LABEL_DEAFULT_PARAM(type, name, #name, val)
|
||||
|
||||
#endif // __REST_H__
|
||||
|
@@ -119,6 +119,10 @@ public:
|
||||
return !(*this > other);
|
||||
}
|
||||
|
||||
Maybe<std::string> calculateSubnetStart(int subnet_value);
|
||||
|
||||
Maybe<std::string> calculateSubnetEnd(int subnet_value);
|
||||
|
||||
// Checks if the IP address is in the range [left, right] inclusive.
|
||||
// All IPAddrs must be of the same kind, or the result is false.
|
||||
bool isInRange(const IPAddr &left, const IPAddr &right) const;
|
||||
@@ -149,6 +153,14 @@ private:
|
||||
// Additional fields to be used by ConnKey class and placed here to save space, IPAddr class should ignore them.
|
||||
IPProto proto;
|
||||
PortNumber port;
|
||||
|
||||
Maybe<std::string> calculateSubnetStartV4(int subnet_value);
|
||||
|
||||
Maybe<std::string> calculateSubnetEndV4(int subnet_value);
|
||||
|
||||
Maybe<std::string> calculateSubnetStartV6(int subnet_value);
|
||||
|
||||
Maybe<std::string> calculateSubnetEndV6(int subnet_value);
|
||||
};
|
||||
|
||||
namespace ConnKeyUtil {
|
||||
@@ -171,12 +183,30 @@ public:
|
||||
static Maybe<CustomRange<RangeType>>
|
||||
createRange(const std::string &maybe_range)
|
||||
{
|
||||
std::string start_range = maybe_range.substr(0, maybe_range.find("-"));
|
||||
std::string end_range = maybe_range.substr(maybe_range.find("-") + 1);
|
||||
if (end_range.empty()) {
|
||||
end_range = start_range;
|
||||
}
|
||||
std::string start_range;
|
||||
std::string end_range;
|
||||
size_t delimiter_position;
|
||||
|
||||
if ((delimiter_position = maybe_range.find("-")) != std::string::npos) {
|
||||
// If it's a range.
|
||||
start_range = maybe_range.substr(0, delimiter_position);
|
||||
end_range = maybe_range.substr(delimiter_position + 1);
|
||||
if (end_range.empty()) {
|
||||
end_range = start_range;
|
||||
}
|
||||
} else if ((delimiter_position = maybe_range.find("/")) != std::string::npos) {
|
||||
// If it's a subnet.
|
||||
IPAddr ip;
|
||||
ConnKeyUtil::fromString((maybe_range.substr(0, delimiter_position)), ip);
|
||||
std::string subnet = maybe_range.substr(delimiter_position + 1);
|
||||
int subnet_value = std::stoi(subnet);
|
||||
start_range = ip.calculateSubnetStart(subnet_value).unpack();
|
||||
end_range = ip.calculateSubnetEnd(subnet_value).unpack();
|
||||
} else {
|
||||
// If it's a single IP.
|
||||
start_range = maybe_range;
|
||||
end_range = maybe_range;
|
||||
}
|
||||
RangeType _start;
|
||||
if (!ConnKeyUtil::fromString(start_range, _start)) {
|
||||
return genError("Error in start value of custom range, value: " + start_range);
|
||||
@@ -188,7 +218,7 @@ public:
|
||||
}
|
||||
|
||||
if (_start > _end) {
|
||||
return genError("Error in creating custom range, invalid range: " + maybe_range);
|
||||
return genError("Error in creating custom range, invalid range: " + maybe_range);
|
||||
}
|
||||
|
||||
return CustomRange(_start, _end);
|
||||
|
@@ -49,7 +49,8 @@ static const map<string, Intelligence::ObjectType> object_names = {
|
||||
{ "zone", Intelligence::ObjectType::ZONE },
|
||||
{ "policyPackage", Intelligence::ObjectType::POLICY_PACKAGE },
|
||||
{ "configuration", Intelligence::ObjectType::CONFIGURATION },
|
||||
{ "session", Intelligence::ObjectType::SESSION }
|
||||
{ "session", Intelligence::ObjectType::SESSION },
|
||||
{ "shortLived", Intelligence::ObjectType::SHORTLIVED }
|
||||
};
|
||||
|
||||
class InvalidationRegistration
|
||||
@@ -128,7 +129,9 @@ public:
|
||||
void
|
||||
performCallBacks(const Invalidation &invalidation) const override
|
||||
{
|
||||
dbgDebug(D_INTELLIGENCE) << "Looking for callbacks for invalidation " << invalidation.genObject();
|
||||
for (auto ®isted_invalidation : callbacks) {
|
||||
dbgTrace(D_INTELLIGENCE) << "Checking against: " << registed_invalidation.second.first.genObject();
|
||||
performCallBacksImpl(invalidation, registed_invalidation.second);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,138 @@
|
||||
// Copyright (C) 2023 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (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 "intelligence_is_v2/intelligence_query_v2.h"
|
||||
|
||||
#include "cptest.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace testing;
|
||||
|
||||
USE_DEBUG_FLAG(D_INTELLIGENCE);
|
||||
|
||||
TEST(IntelligenceQueryTestV2, genJsonPrettySingleRequest) {
|
||||
QueryRequest request(Condition::EQUALS, "phase", "testing", true);
|
||||
IntelligenceQuery<int> query(request, true);
|
||||
|
||||
std::string expected = "{\n"
|
||||
" \"limit\": 20,\n"
|
||||
" \"fullResponse\": true,\n"
|
||||
" \"query\": {\n"
|
||||
" \"operator\": \"equals\",\n"
|
||||
" \"key\": \"mainAttributes.phase\",\n"
|
||||
" \"value\": \"testing\"\n"
|
||||
" }\n"
|
||||
"}";
|
||||
|
||||
EXPECT_EQ(*query.genJson(), expected);
|
||||
}
|
||||
|
||||
TEST(IntelligenceQueryTestV2, genJsonUnprettySingleRequest) {
|
||||
QueryRequest request(Condition::EQUALS, "phase", "testing", true);
|
||||
IntelligenceQuery<int> query(request, false);
|
||||
std::string expected = "{"
|
||||
"\"limit\":20,"
|
||||
"\"fullResponse\":true,"
|
||||
"\"query\":{"
|
||||
"\"operator\":\"equals\","
|
||||
"\"key\":\"mainAttributes.phase\","
|
||||
"\"value\":\"testing\""
|
||||
"}}";
|
||||
|
||||
EXPECT_EQ(*query.genJson(), expected);
|
||||
}
|
||||
|
||||
TEST(IntelligenceQueryTestV2, genJsonUnprettySingleRequestSpaces) {
|
||||
QueryRequest request(Condition::EQUALS, "ph ase", "te sti\" n g\\", true);
|
||||
IntelligenceQuery<int> query(request, false);
|
||||
std::string expected = "{"
|
||||
"\"limit\":20,"
|
||||
"\"fullResponse\":true,"
|
||||
"\"query\":{"
|
||||
"\"operator\":\"equals\","
|
||||
"\"key\":\"mainAttributes.ph ase\","
|
||||
"\"value\":\"te sti\\\" n g\\\\\""
|
||||
"}}";
|
||||
|
||||
EXPECT_EQ(*query.genJson(), expected);
|
||||
}
|
||||
|
||||
TEST(IntelligenceQueryTestV2, genJsonPrettyBulkRequests) {
|
||||
QueryRequest request1(Condition::EQUALS, "phase", "testing", true);
|
||||
QueryRequest request2(Condition::EQUALS, "height", "testing", 25);
|
||||
std::vector<QueryRequest> requests = {request1, request2};
|
||||
IntelligenceQuery<int> query(requests, true);
|
||||
|
||||
std::string expected = "{\n"
|
||||
" \"queries\": [\n"
|
||||
" {\n"
|
||||
" \"query\": {\n"
|
||||
" \"limit\": 20,\n"
|
||||
" \"fullResponse\": true,\n"
|
||||
" \"query\": {\n"
|
||||
" \"operator\": \"equals\",\n"
|
||||
" \"key\": \"mainAttributes.phase\",\n"
|
||||
" \"value\": \"testing\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"index\": 0\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" \"query\": {\n"
|
||||
" \"limit\": 20,\n"
|
||||
" \"fullResponse\": true,\n"
|
||||
" \"query\": {\n"
|
||||
" \"operator\": \"equals\",\n"
|
||||
" \"key\": \"mainAttributes.height\",\n"
|
||||
" \"value\": \"testing\"\n"
|
||||
" }\n"
|
||||
" },\n"
|
||||
" \"index\": 1\n"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
"}";
|
||||
|
||||
EXPECT_EQ(*query.genJson(), expected);
|
||||
}
|
||||
|
||||
TEST(IntelligenceQueryTestV2, genJsonUnprettyBulkRequest) {
|
||||
QueryRequest request1(Condition::EQUALS, "phase", "testing", true);
|
||||
QueryRequest request2(Condition::EQUALS, "height", "testing", 25);
|
||||
std::vector<QueryRequest> requests = {request1, request2};
|
||||
IntelligenceQuery<int> query(requests, false);
|
||||
|
||||
std::string expected = "{"
|
||||
"\"queries\":[{"
|
||||
"\"query\":{"
|
||||
"\"limit\":20,"
|
||||
"\"fullResponse\":true,"
|
||||
"\"query\":{"
|
||||
"\"operator\":\"equals\","
|
||||
"\"key\":\"mainAttributes.phase\","
|
||||
"\"value\":\"testing\""
|
||||
"}},"
|
||||
"\"index\":0"
|
||||
"},{"
|
||||
"\"query\":{"
|
||||
"\"limit\":20,"
|
||||
"\"fullResponse\":true,"
|
||||
"\"query\":{"
|
||||
"\"operator\":\"equals\","
|
||||
"\"key\":\"mainAttributes.height\","
|
||||
"\"value\":\"testing\""
|
||||
"}},"
|
||||
"\"index\":1"
|
||||
"}]}";
|
||||
|
||||
EXPECT_EQ(*query.genJson(), expected);
|
||||
}
|
448
core/intelligence_is_v2/intelligence_is_v2_ut/invalidation_ut.cc
Normal file
448
core/intelligence_is_v2/intelligence_is_v2_ut/invalidation_ut.cc
Normal file
@@ -0,0 +1,448 @@
|
||||
#include "intelligence_invalidation.h"
|
||||
|
||||
#include "cptest.h"
|
||||
#include "mock/mock_messaging.h"
|
||||
#include "mock/mock_mainloop.h"
|
||||
#include "mock/mock_time_get.h"
|
||||
#include "mock/mock_rest_api.h"
|
||||
#include "mock/mock_agent_details.h"
|
||||
#include "intelligence_comp_v2.h"
|
||||
#include "config_component.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace Intelligence;
|
||||
using namespace testing;
|
||||
|
||||
static const string invalidation_uri = "/api/v2/intelligence/invalidation";
|
||||
|
||||
TEST(InvalidationBasic, SettersAndGetters)
|
||||
{
|
||||
Invalidation invalidation("aaa");
|
||||
|
||||
EXPECT_EQ(invalidation.getClassifier(ClassifierType::CLASS), "aaa");
|
||||
EXPECT_EQ(invalidation.getClassifier(ClassifierType::CATEGORY), "");
|
||||
EXPECT_EQ(invalidation.getClassifier(ClassifierType::FAMILY), "");
|
||||
EXPECT_EQ(invalidation.getClassifier(ClassifierType::GROUP), "");
|
||||
EXPECT_EQ(invalidation.getClassifier(ClassifierType::ORDER), "");
|
||||
EXPECT_EQ(invalidation.getClassifier(ClassifierType::KIND), "");
|
||||
|
||||
EXPECT_FALSE(invalidation.getStringAttr("attr1").ok());
|
||||
EXPECT_FALSE(invalidation.getStringSetAttr("attr2").ok());
|
||||
EXPECT_FALSE(invalidation.getSourceId().ok());
|
||||
EXPECT_FALSE(invalidation.getObjectType().ok());
|
||||
|
||||
set<string> vals = { "2", "3" };
|
||||
|
||||
invalidation
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setStringAttr("attr1", "1")
|
||||
.setStringSetAttr("attr2", vals)
|
||||
.setSourceId("id")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
EXPECT_EQ(invalidation.getClassifier(ClassifierType::CATEGORY), "bbb");
|
||||
EXPECT_EQ(invalidation.getClassifier(ClassifierType::FAMILY), "ccc");
|
||||
EXPECT_EQ(invalidation.getStringAttr("attr1").unpack(), "1");
|
||||
EXPECT_EQ(invalidation.getStringSetAttr("attr2").unpack(), vals);
|
||||
EXPECT_EQ(invalidation.getSourceId().unpack(), "id");
|
||||
EXPECT_EQ(invalidation.getObjectType().unpack(), Intelligence::ObjectType::ASSET);
|
||||
}
|
||||
|
||||
TEST(InvalidationBasic, Matching)
|
||||
{
|
||||
set<string> vals = { "2", "3" };
|
||||
auto base_invalidation = Invalidation("aaa")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setStringAttr("attr1", "1")
|
||||
.setStringSetAttr("attr2", vals);
|
||||
|
||||
|
||||
auto matching_invalidation = Invalidation("aaa")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setClassifier(ClassifierType::GROUP, "ddd")
|
||||
.setStringAttr("attr1", "1")
|
||||
.setStringSetAttr("attr2", vals)
|
||||
.setStringAttr("attr3", "6")
|
||||
.setSourceId("id")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
EXPECT_TRUE(base_invalidation.matches(matching_invalidation));
|
||||
|
||||
auto missing_attr_invalidation = Invalidation("aaa")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setClassifier(ClassifierType::GROUP, "ddd")
|
||||
.setStringAttr("attr1", "1")
|
||||
.setStringAttr("attr2", "2")
|
||||
.setStringAttr("attr3", "6")
|
||||
.setSourceId("id")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
EXPECT_FALSE(base_invalidation.matches(missing_attr_invalidation));
|
||||
|
||||
set<string> vals2 = { "1", "5" };
|
||||
auto has_extra_value_invalidation = Invalidation("aaa")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setClassifier(ClassifierType::GROUP, "ddd")
|
||||
.setStringSetAttr("attr1", vals2)
|
||||
.setStringSetAttr("attr2", vals)
|
||||
.setStringAttr("attr3", "6")
|
||||
.setSourceId("id")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
EXPECT_TRUE(base_invalidation.matches(has_extra_value_invalidation));
|
||||
}
|
||||
|
||||
class IntelligenceInvalidation : public Test
|
||||
{
|
||||
public:
|
||||
IntelligenceInvalidation() : i_intelligence(Singleton::Consume<I_Intelligence_IS_V2>::from(intelligence))
|
||||
{
|
||||
EXPECT_CALL(
|
||||
mock_ml,
|
||||
addRecurringRoutine(I_MainLoop::RoutineType::System, chrono::microseconds(7200000000), _, _, _)
|
||||
).WillRepeatedly(Return(0));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_ml,
|
||||
addRecurringRoutine(I_MainLoop::RoutineType::System, _, _, "Sending intelligence invalidation", _)
|
||||
).WillRepeatedly(DoAll(SaveArg<2>(&routine), Return(0)));
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_rest,
|
||||
mockRestCall(_, "new-invalidation/source/invalidation", _)
|
||||
).WillRepeatedly(
|
||||
WithArg<2>(Invoke(this, &IntelligenceInvalidation::saveRestServerCB))
|
||||
);
|
||||
|
||||
EXPECT_CALL(
|
||||
mock_rest,
|
||||
getListeningPort()
|
||||
).WillRepeatedly(Return(7000));
|
||||
|
||||
conf.preload();
|
||||
intelligence.preload();
|
||||
intelligence.init();
|
||||
}
|
||||
|
||||
bool
|
||||
saveRestServerCB(const unique_ptr<RestInit> &p)
|
||||
{
|
||||
mock_invalidation = p->getRest();
|
||||
return true;
|
||||
}
|
||||
|
||||
StrictMock<MockMessaging> messaging_mock;
|
||||
StrictMock<MockMainLoop> mock_ml;
|
||||
NiceMock<MockTimeGet> mock_time;
|
||||
NiceMock<MockAgentDetails> mock_details;
|
||||
StrictMock<MockRestApi> mock_rest;
|
||||
ConfigComponent conf;
|
||||
::Environment env;
|
||||
IntelligenceComponentV2 intelligence;
|
||||
I_Intelligence_IS_V2 *i_intelligence;
|
||||
function<void(const Invalidation &)> callback =
|
||||
[this] (const Invalidation &incoming) { recieved_invalidations.push_back(incoming); };
|
||||
vector<Invalidation> recieved_invalidations;
|
||||
unique_ptr<ServerRest> mock_invalidation;
|
||||
I_MainLoop::Routine routine;
|
||||
};
|
||||
|
||||
TEST_F(IntelligenceInvalidation, sending_incomplete_invalidation)
|
||||
{
|
||||
auto invalidation = Invalidation("aaa")
|
||||
.setStringAttr("attr2", "2")
|
||||
.setSourceId("id")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
EXPECT_FALSE(invalidation.report(i_intelligence));
|
||||
}
|
||||
|
||||
TEST_F(IntelligenceInvalidation, sending_public_invalidation)
|
||||
{
|
||||
auto invalidation = Invalidation("aaa")
|
||||
.setStringAttr("attr2", "2")
|
||||
.setSourceId("id")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
string invalidation_json;
|
||||
EXPECT_CALL(
|
||||
messaging_mock,
|
||||
sendMessage(false, _, I_Messaging::Method::POST, invalidation_uri, _, _, true, MessageTypeTag::INTELLIGENCE)
|
||||
).WillOnce(DoAll(SaveArg<1>(&invalidation_json), Return(string())));
|
||||
EXPECT_TRUE(invalidation.report(i_intelligence));
|
||||
|
||||
string expected_json =
|
||||
"{ \"invalidations\": [ { "
|
||||
"\"class\": \"aaa\", "
|
||||
"\"category\": \"bbb\", "
|
||||
"\"family\": \"ccc\", "
|
||||
"\"objectType\": \"asset\", "
|
||||
"\"sourceId\": \"id\", "
|
||||
"\"mainAttributes\": [ { \"attr2\": \"2\" } ]"
|
||||
" } ] }";
|
||||
EXPECT_EQ(invalidation_json, expected_json);
|
||||
}
|
||||
|
||||
TEST_F(IntelligenceInvalidation, sending_private_invalidation)
|
||||
{
|
||||
auto invalidation = Invalidation("aaa")
|
||||
.setStringAttr("attr2", "2")
|
||||
.setSourceId("id")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
|
||||
stringstream configuration;
|
||||
configuration << "{";
|
||||
configuration << " \"agentSettings\":[";
|
||||
configuration << " {\"key\":\"agent.config.useLocalIntelligence\",\"id\":\"id1\",\"value\":\"true\"}";
|
||||
configuration << " ],";
|
||||
configuration << " \"intelligence\":{";
|
||||
configuration << " \"local intelligence server ip\":\"127.0.0.1\",";
|
||||
configuration << " \"local intelligence server primary port\":9090";
|
||||
configuration << " }";
|
||||
configuration << "}";
|
||||
Singleton::Consume<Config::I_Config>::from(conf)->loadConfiguration(configuration);
|
||||
|
||||
string invalidation_json;
|
||||
EXPECT_CALL(
|
||||
messaging_mock,
|
||||
sendMessage(false, _, I_Messaging::Method::POST, "127.0.0.1", 9090, _, invalidation_uri, _, _, _)
|
||||
).WillOnce(DoAll(SaveArg<1>(&invalidation_json), Return(string())));
|
||||
EXPECT_TRUE(invalidation.report(i_intelligence));
|
||||
|
||||
string expected_json =
|
||||
"{ \"invalidations\": [ { "
|
||||
"\"class\": \"aaa\", "
|
||||
"\"category\": \"bbb\", "
|
||||
"\"family\": \"ccc\", "
|
||||
"\"objectType\": \"asset\", "
|
||||
"\"sourceId\": \"id\", "
|
||||
"\"mainAttributes\": [ { \"attr2\": \"2\" } ]"
|
||||
" } ] }";
|
||||
EXPECT_EQ(invalidation_json, expected_json);
|
||||
}
|
||||
|
||||
TEST_F(IntelligenceInvalidation, register_for_invalidation)
|
||||
{
|
||||
stringstream configuration;
|
||||
configuration << "{";
|
||||
configuration << " \"agentSettings\":[";
|
||||
configuration << " {\"key\":\"agent.config.useLocalIntelligence\",\"id\":\"id1\",\"value\":\"true\"}";
|
||||
configuration << " ],";
|
||||
configuration << " \"intelligence\":{";
|
||||
configuration << " \"local intelligence server ip\":\"127.0.0.1\",";
|
||||
configuration << " \"local intelligence server primary port\":9090";
|
||||
configuration << " }";
|
||||
configuration << "}";
|
||||
Singleton::Consume<Config::I_Config>::from(conf)->loadConfiguration(configuration);
|
||||
|
||||
auto invalidation = Invalidation("aaa")
|
||||
.setStringAttr("attr2", "2")
|
||||
.setSourceId("id")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
string body;
|
||||
EXPECT_CALL(
|
||||
messaging_mock,
|
||||
sendMessage(_, _, _, "127.0.0.1", 9090, _, "/api/v2/intelligence/invalidation/register", _, _, _)
|
||||
).WillOnce(DoAll(
|
||||
SaveArg<1>(&body),
|
||||
Return(string())
|
||||
));
|
||||
|
||||
EXPECT_NE(i_intelligence->registerInvalidation(invalidation, callback), 0);
|
||||
|
||||
EXPECT_THAT(body, HasSubstr("\"url\": \"http://127.0.0.1:7000/set-new-invalidation\""));
|
||||
EXPECT_THAT(body, HasSubstr("\"apiVersion\": \"v2\", \"communicationType\": \"sync\""));
|
||||
EXPECT_THAT(body, HasSubstr("\"mainAttributes\": [ { \"attr2\": \"2\" } ]"));
|
||||
}
|
||||
|
||||
TEST_F(IntelligenceInvalidation, invalidation_callback)
|
||||
{
|
||||
stringstream configuration;
|
||||
configuration << "{";
|
||||
configuration << " \"agentSettings\":[";
|
||||
configuration << " {\"key\":\"agent.config.useLocalIntelligence\",\"id\":\"id1\",\"value\":\"true\"}";
|
||||
configuration << " ],";
|
||||
configuration << " \"intelligence\":{";
|
||||
configuration << " \"local intelligence server ip\":\"127.0.0.1\",";
|
||||
configuration << " \"local intelligence server primary port\":9090";
|
||||
configuration << " }";
|
||||
configuration << "}";
|
||||
Singleton::Consume<Config::I_Config>::from(conf)->loadConfiguration(configuration);
|
||||
|
||||
auto invalidation = Invalidation("aaa")
|
||||
.setStringAttr("attr2", "2")
|
||||
.setSourceId("id")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
EXPECT_CALL(
|
||||
messaging_mock,
|
||||
sendMessage(_, _, _, "127.0.0.1", 9090, _, "/api/v2/intelligence/invalidation/register", _, _, _)
|
||||
).WillOnce(Return(string()));
|
||||
|
||||
EXPECT_NE(i_intelligence->registerInvalidation(invalidation, callback), 0);
|
||||
|
||||
set<string> vals = { "1", "5", "2" };
|
||||
auto invalidation2 = Invalidation("aaa")
|
||||
.setStringSetAttr("attr2", vals)
|
||||
.setSourceId("id")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
stringstream json;
|
||||
json << invalidation2.genObject();
|
||||
mock_invalidation->performRestCall(json);
|
||||
|
||||
EXPECT_EQ(recieved_invalidations.size(), 1);
|
||||
EXPECT_EQ(recieved_invalidations[0].getStringSetAttr("attr2").unpack(), vals);
|
||||
}
|
||||
|
||||
TEST_F(IntelligenceInvalidation, delete_invalidation_callback)
|
||||
{
|
||||
stringstream configuration;
|
||||
configuration << "{";
|
||||
configuration << " \"agentSettings\":[";
|
||||
configuration << " {\"key\":\"agent.config.useLocalIntelligence\",\"id\":\"id1\",\"value\":\"true\"}";
|
||||
configuration << " ],";
|
||||
configuration << " \"intelligence\":{";
|
||||
configuration << " \"local intelligence server ip\":\"127.0.0.1\",";
|
||||
configuration << " \"local intelligence server primary port\":9090";
|
||||
configuration << " }";
|
||||
configuration << "}";
|
||||
Singleton::Consume<Config::I_Config>::from(conf)->loadConfiguration(configuration);
|
||||
|
||||
auto invalidation = Invalidation("aaa")
|
||||
.setStringAttr("attr2", "2")
|
||||
.setSourceId("id")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
EXPECT_CALL(
|
||||
messaging_mock,
|
||||
sendMessage(_, _, _, "127.0.0.1", 9090, _, "/api/v2/intelligence/invalidation/register", _, _, _)
|
||||
).WillOnce(Return(string()));
|
||||
|
||||
auto callback_id = i_intelligence->registerInvalidation(invalidation, callback);
|
||||
i_intelligence->unregisterInvalidation(*callback_id);
|
||||
|
||||
set<string> vals = { "1", "5", "2" };
|
||||
auto invalidation2 = Invalidation("aaa")
|
||||
.setStringSetAttr("attr2", vals)
|
||||
.setSourceId("id")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
stringstream json;
|
||||
json << invalidation2.genObject();
|
||||
mock_invalidation->performRestCall(json);
|
||||
|
||||
EXPECT_EQ(recieved_invalidations.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(IntelligenceInvalidation, invalidation_short_handling)
|
||||
{
|
||||
stringstream configuration;
|
||||
configuration << "{";
|
||||
configuration << " \"agentSettings\":[";
|
||||
configuration << " {\"key\":\"agent.config.useLocalIntelligence\",\"id\":\"id1\",\"value\":\"true\"}";
|
||||
configuration << " ],";
|
||||
configuration << " \"intelligence\":{";
|
||||
configuration << " \"local intelligence server ip\":\"127.0.0.1\",";
|
||||
configuration << " \"local intelligence server primary port\":9090";
|
||||
configuration << " }";
|
||||
configuration << "}";
|
||||
Singleton::Consume<Config::I_Config>::from(conf)->loadConfiguration(configuration);
|
||||
|
||||
auto invalidation = Invalidation("aaa")
|
||||
.setStringAttr("attr2", "2")
|
||||
.setSourceId("id")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
EXPECT_CALL(
|
||||
messaging_mock,
|
||||
sendMessage(_, _, _, "127.0.0.1", 9090, _, "/api/v2/intelligence/invalidation/register", _, _, _)
|
||||
).WillOnce(Return(string()));
|
||||
invalidation.startListening(i_intelligence, callback);
|
||||
|
||||
invalidation.stopListening(i_intelligence);
|
||||
|
||||
set<string> vals = { "1", "5", "2" };
|
||||
auto invalidation2 = Invalidation("aaa")
|
||||
.setStringSetAttr("attr2", vals)
|
||||
.setSourceId("id")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
stringstream json;
|
||||
json << invalidation2.genObject();
|
||||
mock_invalidation->performRestCall(json);
|
||||
|
||||
EXPECT_EQ(recieved_invalidations.size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(IntelligenceInvalidation, routine_registration)
|
||||
{
|
||||
stringstream configuration;
|
||||
configuration << "{";
|
||||
configuration << " \"agentSettings\":[";
|
||||
configuration << " {\"key\":\"agent.config.useLocalIntelligence\",\"id\":\"id1\",\"value\":\"true\"}";
|
||||
configuration << " ],";
|
||||
configuration << " \"intelligence\":{";
|
||||
configuration << " \"local intelligence server ip\":\"127.0.0.1\",";
|
||||
configuration << " \"local intelligence server primary port\":9090";
|
||||
configuration << " }";
|
||||
configuration << "}";
|
||||
Singleton::Consume<Config::I_Config>::from(conf)->loadConfiguration(configuration);
|
||||
|
||||
routine();
|
||||
|
||||
auto invalidation = Invalidation("aaa")
|
||||
.setStringAttr("attr2", "2")
|
||||
.setSourceId("id")
|
||||
.setClassifier(ClassifierType::FAMILY, "ccc")
|
||||
.setClassifier(ClassifierType::CATEGORY, "bbb")
|
||||
.setObjectType(Intelligence::ObjectType::ASSET);
|
||||
|
||||
EXPECT_CALL(
|
||||
messaging_mock,
|
||||
sendMessage(_, _, _, "127.0.0.1", 9090, _, "/api/v2/intelligence/invalidation/register", _, _, _)
|
||||
).WillOnce(Return(string()));
|
||||
|
||||
i_intelligence->registerInvalidation(invalidation, callback);
|
||||
|
||||
string body;
|
||||
EXPECT_CALL(
|
||||
messaging_mock,
|
||||
sendMessage(_, _, _, "127.0.0.1", 9090, _, "/api/v2/intelligence/invalidation/register", _, _, _)
|
||||
).WillOnce(DoAll(
|
||||
SaveArg<1>(&body),
|
||||
Return(string())
|
||||
));
|
||||
|
||||
routine();
|
||||
|
||||
EXPECT_THAT(body, HasSubstr("\"url\": \"http://127.0.0.1:7000/set-new-invalidation\""));
|
||||
EXPECT_THAT(body, HasSubstr("\"apiVersion\": \"v2\", \"communicationType\": \"sync\""));
|
||||
EXPECT_THAT(body, HasSubstr("\"mainAttributes\": [ { \"attr2\": \"2\" } ]"));
|
||||
}
|
132
core/intelligence_is_v2/intelligence_is_v2_ut/json_stream_ut.cc
Normal file
132
core/intelligence_is_v2/intelligence_is_v2_ut/json_stream_ut.cc
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright (C) 2023 Check Point Software Technologies Ltd. All rights reserved.
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (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 "intelligence_is_v2/json_stream.h"
|
||||
|
||||
#include "cptest.h"
|
||||
#include "cereal/archives/json.hpp"
|
||||
#include "boost/algorithm/string.hpp"
|
||||
#include "boost/format.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace testing;
|
||||
|
||||
USE_DEBUG_FLAG(D_INTELLIGENCE);
|
||||
|
||||
string
|
||||
addSlashesToSpecialChars(const string &input)
|
||||
{
|
||||
string output;
|
||||
for(auto c : input)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
output += "\\n";
|
||||
break;
|
||||
case '\t':
|
||||
output += "\\t";
|
||||
break;
|
||||
case '\"':
|
||||
case '\\':
|
||||
output += '\\';
|
||||
//no break
|
||||
default:
|
||||
output += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void
|
||||
testJsonStream(const string &key, const string &value, bool is_pretty)
|
||||
{
|
||||
stringstream str_stream;
|
||||
JsonStream json_stream(&str_stream, is_pretty);
|
||||
{
|
||||
cereal::JSONOutputArchive out_ar(json_stream);
|
||||
out_ar.setNextName("regular_num");
|
||||
out_ar.writeName();
|
||||
out_ar.saveValue(15.34);
|
||||
out_ar.setNextName(key.c_str());
|
||||
out_ar.writeName();
|
||||
out_ar.saveValue(value.c_str());
|
||||
}
|
||||
|
||||
string expected_key = addSlashesToSpecialChars(key);
|
||||
string expected_value = addSlashesToSpecialChars(value);
|
||||
|
||||
const string JSON_STRING_WITH_SPACES = "{\n \"regular_num\": 15.34,\n \"%s\": \"%s\"\n}";
|
||||
const string JSON_STRING_WITHOUT_SPACES = "{\"regular_num\":15.34,\"%s\":\"%s\"}";
|
||||
boost::format frmt(is_pretty ? JSON_STRING_WITH_SPACES : JSON_STRING_WITHOUT_SPACES);
|
||||
frmt = frmt % expected_key;
|
||||
frmt = frmt % expected_value;
|
||||
|
||||
string expected = frmt.str();
|
||||
string actual = str_stream.str();
|
||||
EXPECT_EQ(actual, expected);
|
||||
}
|
||||
|
||||
TEST(JsonStreamTest, prettyOneWord)
|
||||
{
|
||||
testJsonStream("regular_key", "regular_value", true);
|
||||
}
|
||||
|
||||
TEST(JsonStreamTest, unprettyOneWord)
|
||||
{
|
||||
testJsonStream("regular_key", "regular_value", false);
|
||||
}
|
||||
|
||||
TEST(JsonStreamTest, prettyTwoWords)
|
||||
{
|
||||
testJsonStream("spaced key", "spaced value", true);
|
||||
}
|
||||
|
||||
TEST(JsonStreamTest, unprettyTwoWords)
|
||||
{
|
||||
testJsonStream("spaced key", "spaced value", false);
|
||||
}
|
||||
|
||||
TEST(JsonStreamTest, prettyWithEnterTab)
|
||||
{
|
||||
testJsonStream("entered\nkey", "tabbed\tvalue", true);
|
||||
}
|
||||
|
||||
TEST(JsonStreamTest, unprettyWithEnterTab)
|
||||
{
|
||||
testJsonStream("entered\nkey", "tabbed\tvalue", false);
|
||||
}
|
||||
|
||||
TEST(JsonStreamTest, prettyWithQout)
|
||||
{
|
||||
testJsonStream("qout \" key\"", "qout \" value\"", true);
|
||||
}
|
||||
|
||||
TEST(JsonStreamTest, unprettyWithQout)
|
||||
{
|
||||
testJsonStream("qout \" key\"", "qout \" value\"", false);
|
||||
}
|
||||
|
||||
TEST(JsonStreamTest, prettyWithSlashQout)
|
||||
{
|
||||
testJsonStream("qout \\\" key\\\"", "qout \\\" value\\\"", true);
|
||||
}
|
||||
|
||||
TEST(JsonStreamTest, unprettyWithSlashQout)
|
||||
{
|
||||
testJsonStream("qout \\\" key\\\"", "qout \\\" value\\\"", false);
|
||||
}
|
@@ -106,7 +106,8 @@ static const map<Intelligence::ObjectType, string> convertObjectType = {
|
||||
{ Intelligence::ObjectType::ZONE, "zone" },
|
||||
{ Intelligence::ObjectType::POLICY_PACKAGE, "policyPackage" },
|
||||
{ Intelligence::ObjectType::CONFIGURATION, "configuration" },
|
||||
{ Intelligence::ObjectType::SESSION, "session" }
|
||||
{ Intelligence::ObjectType::SESSION, "session" },
|
||||
{ Intelligence::ObjectType::SHORTLIVED, "shortLived" }
|
||||
};
|
||||
|
||||
Maybe<string>
|
||||
|
@@ -287,7 +287,10 @@ MainloopComponent::Impl::run()
|
||||
dbgTrace(D_MAINLOOP) <<
|
||||
"Ending execution of corutine. Routine named: " <<
|
||||
curr_iter->second.getRoutineName();
|
||||
if (getTimer()->getMonotonicTime() > stop_time + large_exceeding) {
|
||||
if (
|
||||
getTimer()->getMonotonicTime() > stop_time + large_exceeding &&
|
||||
curr_iter->second.getRoutineName() != "Orchestration runner"
|
||||
) {
|
||||
dbgWarning(D_MAINLOOP)
|
||||
<< "Routine execution exceeded run time. Routine name: "
|
||||
<< curr_iter->second.getRoutineName();
|
||||
@@ -532,9 +535,15 @@ MainloopComponent::Impl::stop(const RoutineMap::iterator &iter)
|
||||
if (iter->second.isActive()) {
|
||||
dbgDebug(D_MAINLOOP) << "Stoping the routine " << iter->first;
|
||||
do_stop = true;
|
||||
auto env = Singleton::Consume<I_Environment>::by<MainloopComponent>()->saveEnvironment();
|
||||
RoutineMap::iterator save_routine = curr_iter;
|
||||
curr_iter = iter;
|
||||
// We are going to let the routine run one last time, so it can throw an exception which will cause the stack
|
||||
// to clean up nicely.
|
||||
iter->second.run();
|
||||
// We swap curr_iter to in case the routine will print debug messages and we can see the real Routine id
|
||||
curr_iter->second.run();
|
||||
curr_iter = save_routine;
|
||||
Singleton::Consume<I_Environment>::by<MainloopComponent>()->loadEnvironment(move(env));
|
||||
do_stop = false;
|
||||
}
|
||||
}
|
||||
|
@@ -216,10 +216,6 @@ public:
|
||||
void
|
||||
init()
|
||||
{
|
||||
proxies = {
|
||||
{ProxyProtocol::HTTP, ProxyData()},
|
||||
{ProxyProtocol::HTTPS, ProxyData()}
|
||||
};
|
||||
initSSL();
|
||||
timer = Singleton::Consume<I_TimeGet>::by<ProtoMessageComp>();
|
||||
encryptor = Singleton::Consume<I_Encryptor>::by<ProtoMessageComp>();
|
||||
@@ -227,8 +223,8 @@ public:
|
||||
msg_buffer = Singleton::Consume<I_MessagingBuffer>::by<ProtoMessageComp>();
|
||||
MessageConnection::timer = Singleton::Consume<I_TimeGet>::by<ProtoMessageComp>();
|
||||
agent_details = Singleton::Consume<I_AgentDetails>::by<ProtoMessageComp>();
|
||||
proxy_configuration = Singleton::Consume<I_ProxyConfiguration>::by<ProtoMessageComp>();
|
||||
agent_details->readAgentDetails();
|
||||
loadAccessToken();
|
||||
|
||||
if (!setActiveFog()) {
|
||||
dbgDebug(D_COMMUNICATION) << "Could not initialize active fog connection";
|
||||
@@ -240,42 +236,6 @@ public:
|
||||
auto cache_timeout = getConfigurationWithDefault<int>(2, "message", "Cache timeout");
|
||||
cache.startExpiration(seconds(cache_timeout), mainloop, timer);
|
||||
|
||||
auto proxy_config = getProfileAgentSetting<string>("agent.config.message.proxy");
|
||||
if (proxy_config.ok()) {
|
||||
agent_details->setProxy(*proxy_config);
|
||||
agent_details->writeAgentDetails();
|
||||
}
|
||||
|
||||
registerConfigLoadCb(
|
||||
[&]()
|
||||
{
|
||||
auto proxy_config = getProfileAgentSetting<string>("agent.config.message.proxy");
|
||||
if (proxy_config.ok()) {
|
||||
is_proxy_configured_via_settings = true;
|
||||
agent_details->setProxy(*proxy_config);
|
||||
agent_details->writeAgentDetails();
|
||||
} else if (is_proxy_configured_via_settings) {
|
||||
is_proxy_configured_via_settings = false;
|
||||
agent_details->setProxy(string(""));
|
||||
agent_details->writeAgentDetails();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
auto load_env_proxy = loadProxy();
|
||||
if (!load_env_proxy.ok()) {
|
||||
dbgDebug(D_COMMUNICATION)
|
||||
<< "Could not initialize load proxy from environment, Error: "
|
||||
<< load_env_proxy.getErr();
|
||||
}
|
||||
|
||||
Singleton::Consume<I_MainLoop>::by<ProtoMessageComp>()->addRecurringRoutine(
|
||||
I_MainLoop::RoutineType::System,
|
||||
seconds(60),
|
||||
[this] () { loadAccessToken(); },
|
||||
"Load access token"
|
||||
);
|
||||
|
||||
auto metrics_debugs_interval =
|
||||
chrono::seconds(getConfigurationWithDefault<uint64_t>(
|
||||
600,
|
||||
@@ -324,30 +284,6 @@ public:
|
||||
MessageConnection::timer = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
loadAccessToken()
|
||||
{
|
||||
filesystem_prefix = getFilesystemPathConfig();
|
||||
dbgTrace(D_COMMUNICATION) << "ProtoMessageComp, file systen prefix: " << filesystem_prefix << endl;
|
||||
auto data_path = getConfigurationWithDefault<string>(
|
||||
filesystem_prefix + "/data/",
|
||||
"encryptor",
|
||||
"Data files directory"
|
||||
);
|
||||
ifstream token_file(data_path + session_token_file_name);
|
||||
if (!token_file.is_open()) {
|
||||
dbgWarning(D_COMMUNICATION) << "Failed to open session token file";
|
||||
return;
|
||||
}
|
||||
stringstream token_steam;
|
||||
token_steam << token_file.rdbuf();
|
||||
|
||||
auto new_token = token_steam.str();
|
||||
if (access_token != new_token) {
|
||||
access_token = new_token;
|
||||
dbgTrace(D_COMMUNICATION) << "Loaded the new token";
|
||||
}
|
||||
}
|
||||
// LCOV_EXCL_START Reason: No proxy for ut
|
||||
void
|
||||
setFogProxy(const string &host, const uint16_t port, ProxyProtocol proto)
|
||||
@@ -355,7 +291,7 @@ public:
|
||||
dbgTrace(D_COMMUNICATION) << "Proxy was set. Proxy: " << host << ":" << port;
|
||||
MessageConnection::proxy_host = host;
|
||||
MessageConnection::proxy_port = port;
|
||||
auto proxy_auth = getProxyCredentials(proto);
|
||||
auto proxy_auth = proxy_configuration->getProxyCredentials(proto);
|
||||
if (proxy_auth.ok()) {
|
||||
MessageConnection::proxy_auth = proxy_auth.unpack();
|
||||
}
|
||||
@@ -368,23 +304,23 @@ public:
|
||||
MessageConnKey fog_key = make_tuple("fog", 0, tag);
|
||||
proxy_protocol = is_secure ? ProxyProtocol::HTTPS : ProxyProtocol::HTTP;
|
||||
|
||||
auto load_env_proxy = loadProxy();
|
||||
auto load_env_proxy = proxy_configuration->loadProxy();
|
||||
if (!load_env_proxy.ok()) {
|
||||
dbgDebug(D_COMMUNICATION)
|
||||
<< "Could not initialize load proxy from environment, Error: "
|
||||
<< load_env_proxy.getErr();
|
||||
}
|
||||
|
||||
if (getProxyExists(proxy_protocol)) {
|
||||
auto proxy_host = getProxyDomain(proxy_protocol);
|
||||
auto proxy_port = getProxyPort(proxy_protocol);
|
||||
if (proxy_configuration->getProxyExists(proxy_protocol)) {
|
||||
auto proxy_host = proxy_configuration->getProxyDomain(proxy_protocol);
|
||||
auto proxy_port = proxy_configuration->getProxyPort(proxy_protocol);
|
||||
if (proxy_host.ok() && proxy_port.ok()) {
|
||||
setFogProxy(proxy_host.unpack(), proxy_port.unpack(), proxy_protocol);
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<MessageConnection> conn = MessageConnection::startNewConnection(
|
||||
host, port, is_secure, tag, getProxyExists(proxy_protocol)
|
||||
host, port, is_secure, tag, proxy_configuration->getProxyExists(proxy_protocol)
|
||||
);
|
||||
if (!conn.ok()) {
|
||||
dbgWarning(D_COMMUNICATION)
|
||||
@@ -403,7 +339,7 @@ public:
|
||||
<< ":"
|
||||
<< port
|
||||
<< " via "
|
||||
<< (getProxyExists(proxy_protocol) ? "proxy, using " : "")
|
||||
<< (proxy_configuration->getProxyExists(proxy_protocol) ? "proxy, using " : "")
|
||||
<< (is_secure ? "secure" : "clear")
|
||||
<< " connection";
|
||||
|
||||
@@ -652,7 +588,7 @@ public:
|
||||
return sendMessage(conn_iter->second, get_reply, body, method, url, headers, err_call_back);
|
||||
}
|
||||
}
|
||||
auto load_env_proxy = loadProxy();
|
||||
auto load_env_proxy = proxy_configuration->loadProxy();
|
||||
if (!load_env_proxy.ok()) return genError(load_env_proxy.getErr());
|
||||
|
||||
Maybe<MessageConnection> conn = MessageConnection::startNewConnection(
|
||||
@@ -682,153 +618,6 @@ public:
|
||||
return sendMessage(active_conn, get_reply, body, method, url, headers, err_call_back);
|
||||
}
|
||||
|
||||
string getAccessToken() override
|
||||
{
|
||||
return access_token;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
getProxyDomain(ProxyProtocol protocol) const override
|
||||
{
|
||||
if (proxies.find(protocol) == proxies.end()) {
|
||||
return genError("Proxy type is not loaded in map, type: " + convertProxyProtocolToString(protocol));
|
||||
}
|
||||
if (proxies.at(protocol).domain.empty()) return genError(
|
||||
convertProxyProtocolToString(protocol) + string(" proxy domain is unset")
|
||||
);
|
||||
return proxies.at(protocol).domain;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
getProxyCredentials(ProxyProtocol protocol) const override
|
||||
{
|
||||
if (proxies.find(protocol) == proxies.end()) {
|
||||
return genError("Proxy type is not loaded in map, type: " + convertProxyProtocolToString(protocol));
|
||||
}
|
||||
if (proxies.at(protocol).auth.empty()) return genError(
|
||||
convertProxyProtocolToString(protocol) + string(" proxy auth is unset")
|
||||
);
|
||||
return proxies.at(protocol).auth;
|
||||
}
|
||||
|
||||
Maybe<uint16_t>
|
||||
getProxyPort(ProxyProtocol protocol) const override
|
||||
{
|
||||
if (proxies.find(protocol) == proxies.end()) {
|
||||
return genError("Proxy type is not loaded in map, type: " + convertProxyProtocolToString(protocol));
|
||||
}
|
||||
if (proxies.at(protocol).port == 0) return genError(
|
||||
convertProxyProtocolToString(protocol) + string(" proxy port is unset")
|
||||
);
|
||||
return proxies.at(protocol).port;
|
||||
}
|
||||
|
||||
bool
|
||||
getProxyExists(ProxyProtocol protocol) const override
|
||||
{
|
||||
if (proxies.find(protocol) == proxies.end()) {
|
||||
dbgInfo(D_COMMUNICATION)
|
||||
<< "Proxy type is not loaded in map, type: "
|
||||
<< convertProxyProtocolToString(protocol);
|
||||
return false;
|
||||
}
|
||||
return proxies.at(protocol).is_exists;
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
getProxyAddress(ProxyProtocol protocol) const override
|
||||
{
|
||||
if (proxies.find(protocol) == proxies.end()) {
|
||||
return genError("Proxy type is not loaded in map, type: " + convertProxyProtocolToString(protocol));
|
||||
}
|
||||
if (proxies.at(protocol).protocol.empty() ||
|
||||
proxies.at(protocol).domain.empty() ||
|
||||
proxies.at(protocol).port == 0) {
|
||||
return genError(
|
||||
string("Can't construct ") +
|
||||
convertProxyProtocolToString(protocol) +
|
||||
string(" proxy address")
|
||||
);
|
||||
}
|
||||
return proxies.at(protocol).protocol +
|
||||
"://" +
|
||||
proxies.at(protocol).domain +
|
||||
":" +
|
||||
to_string(proxies.at(protocol).port);
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
loadProxy() override
|
||||
{
|
||||
if (getConfigurationFlag("orchestration-mode") == "offline_mode") return Maybe<void>();
|
||||
for (const auto &proxy_type : proxies) {
|
||||
auto loaded_proxy = loadProxyType(proxy_type.first);
|
||||
if (!loaded_proxy.ok()) return loaded_proxy;
|
||||
}
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
loadProxyType(ProxyProtocol protocol)
|
||||
{
|
||||
dbgAssert(protocol == ProxyProtocol::HTTP || protocol == ProxyProtocol::HTTPS)
|
||||
<< "Unsupported Proxy Protocol " << static_cast<int>(protocol);
|
||||
|
||||
static const map<ProxyProtocol, string> env_var_name = {
|
||||
{ProxyProtocol::HTTPS, "https_proxy"},
|
||||
{ProxyProtocol::HTTP, "http_proxy"}
|
||||
};
|
||||
auto env_proxy = loadProxyType(env_var_name.at(protocol));
|
||||
if (!env_proxy.ok()) return genError(env_proxy.getErr());
|
||||
if (env_proxy.unpack().empty()) {
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
string protocol_regex = "(http|https)://";
|
||||
const static boost::regex no_auth_proxy_regex(protocol_regex + "(.)*:[0-9]{0,5}(/|)");
|
||||
const static boost::regex auth_proxy_regex(protocol_regex + "(.)*:(.)*@(.)*:[0-9]{0,5}(/|)");
|
||||
|
||||
ProxyData env_proxy_data;
|
||||
env_proxy_data.is_exists = true;
|
||||
string proxy_copy;
|
||||
if (!NGEN::Regex::regexMatch(__FILE__, __LINE__, env_proxy.unpack(), boost::regex(protocol_regex + "(.)*"))) {
|
||||
env_proxy = "http://" + env_proxy.unpack();
|
||||
}
|
||||
proxy_copy.assign(env_proxy.unpack());
|
||||
env_proxy_data.protocol = env_proxy.unpack().substr(0, proxy_copy.find(":"));
|
||||
proxy_copy.erase(0, proxy_copy.find(":") + 3); //remove "http://" or "https://"
|
||||
|
||||
if (NGEN::Regex::regexMatch(__FILE__, __LINE__, env_proxy.unpack(), auth_proxy_regex)) {
|
||||
env_proxy_data.auth = string(&proxy_copy[0], &proxy_copy[proxy_copy.find("@")]);
|
||||
proxy_copy.erase(0, proxy_copy.find("@") + 1); // remove "user:pass@"
|
||||
} else if (!NGEN::Regex::regexMatch(__FILE__, __LINE__, env_proxy.unpack(), no_auth_proxy_regex)) {
|
||||
return genError(string("Provided proxy has wrong syntax: ") + env_proxy.unpack());
|
||||
}
|
||||
env_proxy_data.domain = proxy_copy.substr(0, proxy_copy.find(":"));
|
||||
proxy_copy.erase(0, proxy_copy.find(":") + 1); // remove "host:"
|
||||
env_proxy_data.port = static_cast<uint16_t>(stoi(proxy_copy));
|
||||
|
||||
auto proxy_syntax = verifyProxySyntax(
|
||||
env_proxy_data.protocol,
|
||||
env_proxy_data.auth,
|
||||
env_proxy_data.domain,
|
||||
to_string(env_proxy_data.port),
|
||||
env_proxy.unpack()
|
||||
);
|
||||
if (!proxy_syntax.ok()) return proxy_syntax;
|
||||
if (env_proxy_data == proxies.at(protocol)) {
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
proxies.at(protocol) = env_proxy_data;
|
||||
dbgInfo(D_COMMUNICATION)
|
||||
<< convertProxyProtocolToString(protocol)
|
||||
<< " proxy was successfully loaded, "
|
||||
<< getProxyAddress(protocol).unpack();
|
||||
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
private:
|
||||
Maybe<string>
|
||||
sendMessage(
|
||||
@@ -935,85 +724,6 @@ private:
|
||||
return genError("Failed to send HTTP request");
|
||||
}
|
||||
|
||||
Maybe<void>
|
||||
verifyProxySyntax(
|
||||
const string &protocol,
|
||||
const string &auth,
|
||||
const string &domain,
|
||||
const string &port,
|
||||
const string &env_proxy)
|
||||
{
|
||||
stringstream verify_string;
|
||||
verify_string
|
||||
<< protocol
|
||||
<< "://"
|
||||
<< (!auth.empty() ? auth + string("@") : "")
|
||||
<< domain
|
||||
<< ":"
|
||||
<< port
|
||||
<< (env_proxy.back() == '/' ? "/" : "");
|
||||
|
||||
if (env_proxy.compare(verify_string.str()) != 0) {
|
||||
return genError(string("Provided proxy has the wrong syntax:" ) + env_proxy);
|
||||
}
|
||||
return Maybe<void>();
|
||||
}
|
||||
|
||||
Maybe<string>
|
||||
loadProxyType(const string &proxy_type)
|
||||
{
|
||||
agent_details->readAgentDetails();
|
||||
auto proxy_config = agent_details->getProxy();
|
||||
if (proxy_config.ok()) {
|
||||
if (proxy_config.unpack() == "none") {
|
||||
return Maybe<string>(string());
|
||||
}
|
||||
return proxy_config;
|
||||
}
|
||||
|
||||
#ifdef gaia
|
||||
I_ShellCmd *shell_cmd = Singleton::Consume<I_ShellCmd>::by<ProtoMessageComp>();
|
||||
auto proxy_ip = shell_cmd->getExecOutput("dbget proxy:ip-address| tr -d '\n'");
|
||||
if (!proxy_ip.ok()) return proxy_ip;
|
||||
auto proxy_port = shell_cmd->getExecOutput("dbget proxy:port| tr -d '\n'");
|
||||
if (!proxy_port.ok()) return proxy_port;
|
||||
if (*proxy_port != "" && *proxy_ip != "") return ("http://" + *proxy_ip + ":" + *proxy_port);
|
||||
|
||||
const string umis_file_path(string(getenv("CPDIR")) + "/tmp/umis_objects.C");
|
||||
|
||||
{
|
||||
ifstream umis_file(umis_file_path.c_str());
|
||||
if (!umis_file.good()) return Maybe<string>(string());
|
||||
}
|
||||
|
||||
const string read_umis_cmd = "cat " + umis_file_path + " | grep -w \"";
|
||||
const string parse_value_command = "\" | awk -F \"[ \\t]+\" '{printf $NF}' | tr -d \"()\"";
|
||||
|
||||
auto use_proxy = shell_cmd->getExecOutput(read_umis_cmd + "use_proxy" + parse_value_command);
|
||||
if (!use_proxy.ok())
|
||||
return genError("Failed to read use_proxy from " + umis_file_path + ": " + use_proxy.getErr());
|
||||
|
||||
if (use_proxy.unpack() == "true") {
|
||||
auto umis_proxy_add = shell_cmd->getExecOutput(read_umis_cmd + "proxy_address" + parse_value_command);
|
||||
if (!umis_proxy_add.ok() || *umis_proxy_add == "") return umis_proxy_add;
|
||||
auto umis_proxy_port = shell_cmd->getExecOutput(read_umis_cmd + "proxy_port" + parse_value_command);
|
||||
if (!umis_proxy_port.ok() || *umis_proxy_port == "") return umis_proxy_port;
|
||||
|
||||
return ("http://" + *umis_proxy_add + ":" + *umis_proxy_port);
|
||||
} else {
|
||||
dbgTrace(D_COMMUNICATION) << "Smart Console Proxy is turned off";
|
||||
}
|
||||
return Maybe<string>(string());
|
||||
#else // not gaia
|
||||
char *proxy = getenv(proxy_type.c_str());
|
||||
if (proxy) return string(proxy);
|
||||
|
||||
proxy = getenv(boost::algorithm::to_upper_copy(proxy_type).c_str());
|
||||
if (proxy) return string(proxy);
|
||||
return Maybe<string>(string());
|
||||
#endif // gaia
|
||||
}
|
||||
|
||||
string
|
||||
base64Decode(const string &input) const
|
||||
{
|
||||
@@ -1087,6 +797,8 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
const string &access_token = agent_details->getAccessToken();
|
||||
|
||||
if (!conn.isExternal() && !access_token.empty() && headers.find("Authorization") == std::string::npos) {
|
||||
req.insertHeader("Authorization", "Bearer " + access_token);
|
||||
}
|
||||
@@ -1186,17 +898,6 @@ private:
|
||||
return count;
|
||||
}
|
||||
|
||||
string
|
||||
convertProxyProtocolToString(ProxyProtocol proto) const
|
||||
{
|
||||
switch(proto) {
|
||||
case ProxyProtocol::HTTP: return "http";
|
||||
case ProxyProtocol::HTTPS: return "https";
|
||||
}
|
||||
dbgAssert(false) << "Unsupported Proxy Protocol " << static_cast<int>(proto);
|
||||
return "";
|
||||
}
|
||||
|
||||
Maybe<Method>
|
||||
stringToMethod(const string &name)
|
||||
{
|
||||
@@ -1209,40 +910,19 @@ private:
|
||||
return genError("Cannot convert unknown HTTP method to Enum. Method name: " + name);
|
||||
}
|
||||
|
||||
class ProxyData
|
||||
{
|
||||
public:
|
||||
bool
|
||||
operator==(const ProxyData &other) const
|
||||
{
|
||||
return protocol==other.protocol &&
|
||||
domain==other.domain &&
|
||||
is_exists==other.is_exists &&
|
||||
port==other.port &&
|
||||
auth==other.auth;
|
||||
}
|
||||
|
||||
string protocol = "";
|
||||
string domain = "";
|
||||
string auth = "";
|
||||
bool is_exists = false;
|
||||
uint16_t port = 0;
|
||||
};
|
||||
|
||||
bool is_proxy_configured_via_settings = false;
|
||||
uint64_t number_of_reconnects = 0;
|
||||
uint64_t number_of_reconnect_failures = 0;
|
||||
uint64_t number_of_send_failure = 0;
|
||||
I_AgentDetails *agent_details = nullptr;
|
||||
I_MainLoop *mainloop = nullptr;
|
||||
I_TimeGet *timer = nullptr;
|
||||
I_Encryptor *encryptor = nullptr;
|
||||
I_MessagingBuffer *msg_buffer = nullptr;
|
||||
bool is_proxy_configured_via_settings = false;
|
||||
uint64_t number_of_reconnects = 0;
|
||||
uint64_t number_of_reconnect_failures = 0;
|
||||
uint64_t number_of_send_failure = 0;
|
||||
I_AgentDetails *agent_details = nullptr;
|
||||
I_MainLoop *mainloop = nullptr;
|
||||
I_TimeGet *timer = nullptr;
|
||||
I_Encryptor *encryptor = nullptr;
|
||||
I_MessagingBuffer *msg_buffer = nullptr;
|
||||
I_ProxyConfiguration *proxy_configuration = nullptr;
|
||||
map<MessageConnKey, MessageConnection> active_connections;
|
||||
map<MessageTypeTag, MessageConnKey> tag_to_active_conn_key;
|
||||
map<ProxyProtocol, ProxyData> proxies;
|
||||
ProxyProtocol proxy_protocol;
|
||||
string access_token;
|
||||
TemporaryCache<string, string> cache;
|
||||
static const map<ProxyProtocol, string> proxyProtocolToString;
|
||||
set<HTTPRequestSignature> pending_signatures;
|
||||
@@ -1671,7 +1351,7 @@ MessageConnection::receiveResponse(I_MessageDecoder<T> &decoder)
|
||||
}
|
||||
|
||||
if (connection_closed_count > 0) {
|
||||
dbgInfo(D_COMMUNICATION)
|
||||
dbgTrace(D_COMMUNICATION)
|
||||
<< "Connection was reconnected. Type: "
|
||||
<< tagToString(tag)
|
||||
<< ", number of attempts: "
|
||||
|
@@ -71,6 +71,7 @@ RestConn::parseConn() const
|
||||
string uri;
|
||||
os >> uri;
|
||||
string identifier = uri.substr(uri.find_first_of('/') + 1);
|
||||
dbgDebug(D_API) << "Call identifier: " << identifier;
|
||||
|
||||
uint len = 0;
|
||||
while (true) {
|
||||
@@ -88,6 +89,8 @@ RestConn::parseConn() const
|
||||
}
|
||||
}
|
||||
|
||||
dbgDebug(D_API) << "Message length: " << len;
|
||||
|
||||
if (method=="POST" && len==0) {
|
||||
dbgWarning(D_API) << "No length was found - could be chunked, but we still do not support that";
|
||||
sendResponse("411 Length Required", "");
|
||||
@@ -97,6 +100,8 @@ RestConn::parseConn() const
|
||||
stringstream body;
|
||||
body.str(readSize(len));
|
||||
|
||||
dbgTrace(D_API) << "Message content: " << body.str();
|
||||
|
||||
Maybe<string> res = (method == "POST") ? invoke->invokeRest(identifier, body) : invoke->getSchema(identifier);
|
||||
|
||||
if (res.ok()) {
|
||||
|
@@ -94,7 +94,7 @@ public:
|
||||
|
||||
int res = send(socket_int, data.data() + bytes_sent, data.size() - bytes_sent, MSG_NOSIGNAL);
|
||||
if (res <= 0) {
|
||||
dbgWarning(D_SOCKET) << "Failed to send data";
|
||||
dbgWarning(D_SOCKET) << "Failed to send data, Error: " << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user