diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index bcbf38ec..fdb1e9fa 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -70,6 +70,7 @@ target_link_libraries(pycasbin PRIVATE pybind11::module casbin + nlohmann_json::nlohmann_json ) if(WIN32) diff --git a/casbin/CMakeLists.txt b/casbin/CMakeLists.txt index 9b159b0c..262337c9 100644 --- a/casbin/CMakeLists.txt +++ b/casbin/CMakeLists.txt @@ -75,6 +75,7 @@ add_library(casbin STATIC ${CASBIN_SOURCE_FILES}) target_precompile_headers(casbin PRIVATE pch.h) target_include_directories(casbin PRIVATE ${CASBIN_SOURCE_DIR}) +target_link_libraries(casbin PRIVATE nlohmann_json::nlohmann_json) set_target_properties(casbin PROPERTIES PREFIX "" diff --git a/casbin/data_types.h b/casbin/data_types.h index f49eabad..d4339589 100644 --- a/casbin/data_types.h +++ b/casbin/data_types.h @@ -18,11 +18,12 @@ #include #include #include +#include #include "abac_data.h" namespace casbin { -typedef std::variant> Data; +typedef std::variant, std::shared_ptr> Data; typedef std::vector DataVector; typedef std::initializer_list DataList; typedef std::unordered_map DataMap; diff --git a/casbin/enforcer.cpp b/casbin/enforcer.cpp index d52ac186..1b133bbb 100644 --- a/casbin/enforcer.cpp +++ b/casbin/enforcer.cpp @@ -501,6 +501,14 @@ bool Enforcer::EnforceWithMatcher(const std::string& matcher, const DataList& pa else throw CasbinEnforcerException("Not a valid type"); } + } else if (const auto json_param = std::get_if>(¶m)) { + + auto data_ptr = *json_param; + std::string token_name = r_tokens[i].substr(2, r_tokens[i].size() - 2); + + PushObject(scope, token_name); + PushObjectPropFromJson(scope, *data_ptr, token_name); + PushObjectPropToObject(scope, "r", token_name); } ++i; } @@ -556,7 +564,16 @@ bool Enforcer::EnforceWithMatcher(const std::string& matcher, const DataVector& else throw CasbinEnforcerException("Not a valid type"); } + } else if (const auto json_param = std::get_if>(¶m)) { + + auto data_ptr = *json_param; + std::string token_name = r_tokens[i].substr(2, r_tokens[i].size() - 2); + + PushObject(scope, token_name); + PushObjectPropFromJson(scope, *data_ptr, token_name); + PushObjectPropToObject(scope, "r", token_name); } + ++i; } @@ -601,7 +618,14 @@ bool Enforcer::EnforceWithMatcher(const std::string& matcher, const DataMap& par else throw CasbinEnforcerException("Not a valid type"); } + } else if (const auto json_param = std::get_if>(¶m_data)) { + + auto data_ptr = *json_param; + PushObject(scope, param_name); + PushObjectPropFromJson(scope, *data_ptr, param_name); + PushObjectPropToObject(scope, "r", param_name); } + } bool result = m_enforce(matcher, scope); diff --git a/casbin/model/scope_config.cpp b/casbin/model/scope_config.cpp index 10850a98..7604273e 100644 --- a/casbin/model/scope_config.cpp +++ b/casbin/model/scope_config.cpp @@ -21,7 +21,7 @@ #include "./scope_config.h" - +#include "./exception/illegal_argument_exception.h" namespace casbin { Scope InitializeScope() { @@ -194,6 +194,38 @@ void PushObjectPropToObject(Scope scope, std::string obj, std::string identifier duk_eval_string_noresult(scope, (obj+"len += 1;").c_str()); } +void PushObjectPropFromJson(Scope scope, nlohmann::json& j, std::string objName) { + if (j.is_null()) { + return; + } + + for (auto& curJson: j.items()) { + auto key = curJson.key(); + auto value = curJson.value(); + if (value.is_object()) { + + auto nextJsonName = key + "__"; + PushObject(scope, nextJsonName); + + PushObjectPropFromJson(scope, value, nextJsonName); + + duk_get_global_string(scope, objName.c_str()); + duk_get_global_string(scope, nextJsonName.c_str()); + duk_put_prop_string(scope, -2, key.c_str()); + } else if (value.is_number_float()) { + PushDoublePropToObject(scope, objName, value, key); + } else if (value.is_number_integer()) { + PushIntPropToObject(scope, objName, value, key); + } else if (value.is_string()) { + PushStringPropToObject(scope, objName, value, key); + } else if (value.is_boolean()) { + PushBooleanPropToObject(scope, objName, value, key); + } else { + throw IllegalArgumentException("Unsupported json value type"); + } + } +} + Type CheckType(Scope scope){ if(duk_is_boolean(scope, -1)) return Type::Bool; diff --git a/casbin/model/scope_config.h b/casbin/model/scope_config.h index 54181c5a..213e2508 100644 --- a/casbin/model/scope_config.h +++ b/casbin/model/scope_config.h @@ -69,6 +69,7 @@ void PushDoublePropToObject(Scope scope, std::string obj, double d, std::string void PushStringPropToObject(Scope scope, std::string obj, std::string s, std::string identifier); void PushPointerPropToObject(Scope scope, std::string obj, void * ptr, std::string identifier); void PushObjectPropToObject(Scope scope, std::string obj, std::string identifier); +void PushObjectPropFromJson(Scope scope, nlohmann::json& j, std::string j_name); Type CheckType(Scope scope); bool FetchIdentifier(Scope scope, std::string identifier); unsigned int Size(Scope scope); diff --git a/cmake/modules/FindExtPackages.cmake b/cmake/modules/FindExtPackages.cmake index 4035e075..a731cdcc 100644 --- a/cmake/modules/FindExtPackages.cmake +++ b/cmake/modules/FindExtPackages.cmake @@ -24,6 +24,8 @@ set(CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY ON CACHE BOOL ############################################################################### ### Packages and versions ### +find_package(json 3.7.3 REQUIRED) + if(CASBIN_BUILD_TEST) # googletest # https://github.com/google/googletest diff --git a/cmake/modules/Findjson.cmake b/cmake/modules/Findjson.cmake new file mode 100644 index 00000000..74b48592 --- /dev/null +++ b/cmake/modules/Findjson.cmake @@ -0,0 +1,28 @@ + +# Copyright 2021 The casbin Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +include(FetchContent) + +FetchContent_Declare(json + GIT_REPOSITORY https://github.com/nlohmann/json.git + GIT_TAG v3.7.3) + +set(JSON_BuildTests OFF CACHE INTERNAL "") +FetchContent_GetProperties(json) +FetchContent_MakeAvailable(json) + +if(NOT json_POPULATED) + FetchContent_Populate(json) + add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL) +endif() diff --git a/include/casbin/casbin_helpers.h b/include/casbin/casbin_helpers.h index 01753aac..15e816c2 100644 --- a/include/casbin/casbin_helpers.h +++ b/include/casbin/casbin_helpers.h @@ -540,6 +540,7 @@ namespace casbin { void PushStringPropToObject(Scope scope, std::string obj, std::string s, std::string identifier); void PushPointerPropToObject(Scope scope, std::string obj, void * ptr, std::string identifier); void PushObjectPropToObject(Scope scope, std::string obj, std::string identifier); + void PushObjectPropFromJson(Scope scope, nlohmann::json& j, std::string j_name); Type CheckType(Scope scope); bool FetchIdentifier(Scope scope, std::string identifier); unsigned int Size(Scope scope); diff --git a/include/casbin/casbin_types.h b/include/casbin/casbin_types.h index 8f99d6c0..14efebf4 100644 --- a/include/casbin/casbin_types.h +++ b/include/casbin/casbin_types.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace casbin { @@ -121,7 +122,7 @@ namespace casbin { */ const std::shared_ptr GetDataObject(const AttributeMap& attribs); - typedef std::variant> Data; + typedef std::variant, std::shared_ptr> Data; typedef std::vector DataVector; typedef std::initializer_list DataList; typedef std::unordered_map DataMap; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f7d20571..c81cc4ca 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -51,6 +51,7 @@ if(CASBIN_BUILD_TEST) PRIVATE gtest_main casbin + nlohmann_json::nlohmann_json ) include(GoogleTest) diff --git a/tests/benchmarks/CMakeLists.txt b/tests/benchmarks/CMakeLists.txt index c6a34302..bfd39329 100644 --- a/tests/benchmarks/CMakeLists.txt +++ b/tests/benchmarks/CMakeLists.txt @@ -41,4 +41,5 @@ target_link_libraries( PRIVATE benchmark casbin + nlohmann_json::nlohmann_json ) diff --git a/tests/config_path.h b/tests/config_path.h index d9770c13..abc44cb6 100644 --- a/tests/config_path.h +++ b/tests/config_path.h @@ -51,3 +51,5 @@ static const std::string keymatch_policy_path = relative_path + "/examples/keyma static const std::string priority_model_path = relative_path + "/examples/priority_model.conf"; static const std::string priority_policy_path = relative_path + "/examples/priority_policy.csv"; + +static const std::string abac_model_path = relative_path + "/examples/abac_model.conf"; diff --git a/tests/enforcer_test.cpp b/tests/enforcer_test.cpp index 55159425..78f5ccf9 100644 --- a/tests/enforcer_test.cpp +++ b/tests/enforcer_test.cpp @@ -115,4 +115,61 @@ TEST(TestEnforcer, ABACData) { ASSERT_TRUE(params == data->GetAttributes()); } +TEST(TestEnforcer, JsonData) { + using json = nlohmann::json; + casbin::Scope scope = casbin::InitializeScope(); + casbin::PushObject(scope, "r"); + + json myJson = { + {"DoubleCase", 3.141}, + {"IntegerCase", 2}, + {"BoolenCase", true}, + {"StringCase", "Bob"}, + // {"nothing", nullptr}, + {"x", { + {"y", { + {"z", 1} + } + }, + {"x", 2 + } + } + }, + }; + + casbin::PushObjectPropFromJson(scope, myJson, "r"); + std::string s1 = "r.DoubleCase == 3.141;"; + std::string s2 = "r.IntegerCase == 2;"; + std::string s3 = "r.BoolenCase == true;"; + std::string s4 = "r.StringCase == \"Bob\";"; + std::string s5 = "r.x.y.z == 1;"; + std::string s6 = "r.x.x == 2;"; + + auto EvalAndGetTop = [] (casbin::Scope scope, std::string s) -> bool { + casbin::Eval(scope, s); + return casbin::GetBoolean(scope, -1); + }; + + ASSERT_TRUE(EvalAndGetTop(scope, s1)); + ASSERT_TRUE(EvalAndGetTop(scope, s2)); + ASSERT_TRUE(EvalAndGetTop(scope, s3)); + ASSERT_TRUE(EvalAndGetTop(scope, s4)); + ASSERT_TRUE(EvalAndGetTop(scope, s5)); + ASSERT_TRUE(EvalAndGetTop(scope, s6)); + + s1 = "r.DoubleCase == 3.14;"; + s2 = "r.IntegerCase == 1;"; + s3 = "r.BoolenCase == false;"; + s4 = "r.StringCase == \"BoB\";"; + s5 = "r.x.y.z == 2;"; + s6 = "r.x.x == 1;"; + + ASSERT_TRUE(!EvalAndGetTop(scope, s1)); + ASSERT_TRUE(!EvalAndGetTop(scope, s2)); + ASSERT_TRUE(!EvalAndGetTop(scope, s3)); + ASSERT_TRUE(!EvalAndGetTop(scope, s4)); + ASSERT_TRUE(!EvalAndGetTop(scope, s5)); + ASSERT_TRUE(!EvalAndGetTop(scope, s6)); +} + } // namespace diff --git a/tests/model_enforcer_test.cpp b/tests/model_enforcer_test.cpp index 367bd118..cdbc5361 100644 --- a/tests/model_enforcer_test.cpp +++ b/tests/model_enforcer_test.cpp @@ -504,6 +504,25 @@ TEST(TestModelEnforcer, TestRBACModelWithPattern) { scope = InitializeParams("bob", "/pen2/2", "GET"); TestEnforce(e, scope, true); } + +TEST(TestModelEnforcer, TestABACModelWithJson) { + using json = nlohmann::json; + + json obj = { + {"Owner", "alice"}, + }; + auto objPtr = std::make_shared(obj); + + casbin::DataMap mapParams = {{"sub", "alice"}, {"obj", objPtr}, {"act", "write"}}; + casbin::DataList listParams = {"alice", objPtr, "write"}; + casbin::DataVector vectorParams = {"alice", objPtr, "write"}; + + casbin::Enforcer e(abac_model_path); + + ASSERT_TRUE(e.Enforce(mapParams)); + ASSERT_TRUE(e.Enforce(listParams)); + ASSERT_TRUE(e.Enforce(vectorParams)); +} /* type testCustomRoleManager struct {}