diff --git a/casbin/model/evaluator.cpp b/casbin/model/evaluator.cpp index acf3211f..9b609af0 100644 --- a/casbin/model/evaluator.cpp +++ b/casbin/model/evaluator.cpp @@ -15,6 +15,7 @@ */ #include "casbin/model/evaluator.h" +#include #include #include "casbin/util/util.h" @@ -61,7 +62,37 @@ void ExprtkEvaluator::PushObjectString(const std::string& target, const std::str void ExprtkEvaluator::PushObjectJson(const std::string& target, const std::string& proprity, const nlohmann::json& var) { auto identifier = target + "." + proprity; - // this->symbol_table.add_stringvar(identifier, const_cast(var)); + + // Recursively flatten JSON object into dot-notation identifiers + std::function flatten; + flatten = [&](const std::string& prefix, const nlohmann::json& j) { + if (j.is_object()) { + // Recursively process nested objects + for (auto it = j.begin(); it != j.end(); ++it) { + std::string key = it.key(); + flatten(prefix + "." + key, it.value()); + } + } else if (j.is_string()) { + // Add string value + this->AddIdentifier(prefix, j.get()); + } else if (j.is_number_integer()) { + // Convert integer to string + this->AddIdentifier(prefix, std::to_string(j.get())); + } else if (j.is_number_float()) { + // Convert float to string + this->AddIdentifier(prefix, std::to_string(j.get())); + } else if (j.is_boolean()) { + // Convert boolean to string + this->AddIdentifier(prefix, j.get() ? "true" : "false"); + } else if (j.is_null()) { + // Handle null as empty string + this->AddIdentifier(prefix, ""); + } + // Note: JSON arrays are intentionally not handled by this implementation. + // If an array is encountered, it is silently ignored and no identifier is added. + }; + + flatten(identifier, var); } void ExprtkEvaluator::LoadFunctions() { @@ -144,17 +175,16 @@ std::unordered_map ExprtkEvaluator::requestValues() co std::vector var_list; symbol_table.get_stringvar_list(var_list); std::unordered_map result; - for (const auto& e : var_list ) { - if (e[0] == 'r') { + for (const auto& e : var_list) { + if (e[0] == 'r') { auto token = e.substr(2, e.size() - 2); - auto value = symbol_table.get_stringvar("r." + token)->ref().c_str(); + auto value = symbol_table.get_stringvar("r." + token)->ref().c_str(); result.emplace(token, value); } } return result; } - void ExprtkEvaluator::AddIdentifier(const std::string& identifier, const std::string& var) { if (!symbol_table.symbol_exists(identifier)) { identifiers_[identifier] = std::make_unique(""); diff --git a/tests/enforcer_test.cpp b/tests/enforcer_test.cpp index dc0c1b88..d3a0bf3c 100644 --- a/tests/enforcer_test.cpp +++ b/tests/enforcer_test.cpp @@ -416,4 +416,128 @@ TEST(TestEnforcerEx, TestMapParams) { // ASSERT_TRUE(!EvalAndGetTop(scope, s6)); // } +TEST(TestEnforcer, JsonData) { + using json = nlohmann::json; + + // Create evaluator and initialize + auto evaluator = std::make_shared(); + evaluator->InitialObject("r"); + + // Test simple JSON with various data types + json myJson = {{"DoubleCase", 3.141}, {"IntegerCase", 2}, {"BooleanCase", true}, {"StringCase", "Bob"}, {"x", {{"y", {{"z", 1}}}, {"x", 2}}}}; + + evaluator->PushObjectJson("r", "data", myJson); + + // Test double value (stored as string "3.141000") + ASSERT_TRUE(evaluator->Eval("r.data.DoubleCase == '3.141000'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + // Test integer value (stored as string "2") + ASSERT_TRUE(evaluator->Eval("r.data.IntegerCase == '2'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + // Test boolean value (stored as string "true") + ASSERT_TRUE(evaluator->Eval("r.data.BooleanCase == 'true'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + // Test string value + ASSERT_TRUE(evaluator->Eval("r.data.StringCase == 'Bob'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + // Test nested JSON - x.y.z (stored as string "1") + ASSERT_TRUE(evaluator->Eval("r.data.x.y.z == '1'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + // Test nested JSON - x.x (stored as string "2") + ASSERT_TRUE(evaluator->Eval("r.data.x.x == '2'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + // Test negative cases + ASSERT_TRUE(evaluator->Eval("r.data.DoubleCase == '3.14'")); + ASSERT_FALSE(evaluator->GetBoolean()); + + ASSERT_TRUE(evaluator->Eval("r.data.IntegerCase == '1'")); + ASSERT_FALSE(evaluator->GetBoolean()); + + ASSERT_TRUE(evaluator->Eval("r.data.BooleanCase == 'false'")); + ASSERT_FALSE(evaluator->GetBoolean()); + + ASSERT_TRUE(evaluator->Eval("r.data.StringCase == 'BoB'")); + ASSERT_FALSE(evaluator->GetBoolean()); + + ASSERT_TRUE(evaluator->Eval("r.data.x.y.z == '2'")); + ASSERT_FALSE(evaluator->GetBoolean()); + + ASSERT_TRUE(evaluator->Eval("r.data.x.x == '1'")); + ASSERT_FALSE(evaluator->GetBoolean()); +} + +TEST(TestEnforcer, JsonDataComplex) { + using json = nlohmann::json; + + // Create evaluator and initialize + auto evaluator = std::make_shared(); + evaluator->InitialObject("r"); + + // Test complex nested JSON similar to the issue example + json sub = {{"ID", "zk"}, + {"proxy", "vpn"}, + {"Department", "nlp"}, + {"month", "Jan"}, + {"week", "Mon"}, + {"time", "morning"}, + {"Longitude", "123"}, + {"Latitude", "456"}, + {"Altitude", "789"}, + {"OS", "HarmonyOS"}, + {"CPU", "XeonPlatinum8480+"}, + {"NetworkType", "WLan"}, + {"ProtocolType", "Bluetooth"}, + {"EncryptionType", "3DES"}, + {"ESecurityProtocol", "HTTPS"}}; + + json obj = {{"SecurityLevel", "3"}, {"Source", "ISS"}, {"DistributionMethod", "C"}}; + + evaluator->PushObjectJson("r", "sub", sub); + evaluator->PushObjectJson("r", "obj", obj); + evaluator->PushObjectString("r", "act", "read"); + + // Test sub attributes + ASSERT_TRUE(evaluator->Eval("r.sub.ID == 'zk'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + ASSERT_TRUE(evaluator->Eval("r.sub.proxy == 'vpn'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + ASSERT_TRUE(evaluator->Eval("r.sub.Department == 'nlp'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + ASSERT_TRUE(evaluator->Eval("r.sub.OS == 'HarmonyOS'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + ASSERT_TRUE(evaluator->Eval("r.sub.CPU == 'XeonPlatinum8480+'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + // Test obj attributes + ASSERT_TRUE(evaluator->Eval("r.obj.SecurityLevel == '3'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + ASSERT_TRUE(evaluator->Eval("r.obj.Source == 'ISS'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + ASSERT_TRUE(evaluator->Eval("r.obj.DistributionMethod == 'C'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + // Test act attribute + ASSERT_TRUE(evaluator->Eval("r.act == 'read'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + // Test combined conditions + ASSERT_TRUE(evaluator->Eval("r.sub.ID == 'zk' and r.obj.SecurityLevel == '3' and r.act == 'read'")); + ASSERT_TRUE(evaluator->GetBoolean()); + + ASSERT_TRUE(evaluator->Eval("r.sub.Department == 'nlp' and r.obj.Source == 'ISS'")); + ASSERT_TRUE(evaluator->GetBoolean()); +} + } // namespace