blob: 6d517461e315a27181b02a7b31f7d0727f8b3ce8 [file] [log] [blame]
/*
* 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.
*
* This is a test file showcasing the workflow of casbin::SyncedEnforcer
*/
#include <casbin/casbin.h>
#include <gtest/gtest.h>
#include "config_path.h"
namespace {
std::string global_sub;
std::string global_obj;
std::string global_act;
std::string global_domain;
template <typename T>
std::shared_ptr<casbin::IEvaluator> InitializeParams(const std::string& sub, const std::string& obj, const std::string& act) {
auto evaluator = std::make_shared<T>();
evaluator->InitialObject("r");
// Because of "Short String Optimization", these strings's data is in stack.
// For MSVC compiler, when this stack frame return, these memory will can't access.
// So we need keep this memory accessiable.
global_sub = sub;
global_obj = obj;
global_act = act;
evaluator->PushObjectString("r", "sub", global_sub);
evaluator->PushObjectString("r", "obj", global_obj);
evaluator->PushObjectString("r", "act", global_act);
return evaluator;
}
template <typename T>
std::shared_ptr<casbin::IEvaluator> InitializeParamsWithoutUsers(const std::string& obj, const std::string& act) {
auto evaluator = std::make_shared<T>();
evaluator->InitialObject("r");
global_obj = obj;
global_act = act;
evaluator->PushObjectString("r", "obj", global_obj);
evaluator->PushObjectString("r", "act", global_act);
return evaluator;
}
template <typename T>
std::shared_ptr<casbin::IEvaluator> InitializeParamsWithoutResources(const std::string& sub, const std::string& act) {
auto evaluator = std::make_shared<T>();
evaluator->InitialObject("r");
global_sub = sub;
global_act = act;
evaluator->PushObjectString("r", "sub", global_sub);
evaluator->PushObjectString("r", "act", global_act);
return evaluator;
}
template <typename T>
std::shared_ptr<casbin::IEvaluator> InitializeParamsWithDomains(const std::string& sub, const std::string& domain, const std::string& obj, const std::string& act) {
auto evaluator = std::make_shared<T>();
evaluator->InitialObject("r");
global_sub = sub;
global_obj = obj;
global_act = act;
global_domain = domain;
evaluator->PushObjectString("r", "sub", global_sub);
evaluator->PushObjectString("r", "dom", global_domain);
evaluator->PushObjectString("r", "obj", global_obj);
evaluator->PushObjectString("r", "act", global_act);
return evaluator;
}
void TestSyncFn(casbin::SyncedEnforcer& e, const std::string& sub, const std::string& obj, const std::string& act, bool control) {
bool response = e.Enforce({sub, obj, act});
ASSERT_EQ(response, control);
}
TEST(TestEnforcerSynced, TestSync) {
casbin::SyncedEnforcer e(basic_model_path, basic_policy_path);
using namespace std::literals::chrono_literals;
auto time1 = 200ms;
e.StartAutoLoadPolicy(time1);
TestSyncFn(e, "alice", "data1", "read", true);
TestSyncFn(e, "alice", "data1", "write", false);
TestSyncFn(e, "alice", "data2", "read", false);
TestSyncFn(e, "alice", "data2", "write", false);
TestSyncFn(e, "bob", "data1", "read", false);
TestSyncFn(e, "bob", "data1", "write", false);
TestSyncFn(e, "bob", "data2", "read", false);
TestSyncFn(e, "bob", "data2", "write", true);
std::this_thread::sleep_for(200ms);
e.StopAutoLoadPolicy();
}
TEST(TestEnforcerSynced, TestStopLoadPolicy) {
casbin::SyncedEnforcer e(basic_model_path, basic_policy_path);
using namespace std::literals::chrono_literals;
std::chrono::duration<int64_t, std::nano> t = 5ms;
e.StartAutoLoadPolicy(t);
EXPECT_EQ(e.IsAutoLoadingRunning(), true);
TestSyncFn(e, "alice", "data1", "read", true);
TestSyncFn(e, "alice", "data1", "write", false);
TestSyncFn(e, "alice", "data2", "read", false);
TestSyncFn(e, "alice", "data2", "write", false);
TestSyncFn(e, "bob", "data1", "read", false);
TestSyncFn(e, "bob", "data1", "write", false);
TestSyncFn(e, "bob", "data2", "read", false);
TestSyncFn(e, "bob", "data2", "write", true);
e.StopAutoLoadPolicy();
std::this_thread::sleep_for(10ms);
EXPECT_EQ(e.IsAutoLoadingRunning(), false);
}
TEST(TestEnforcerSynced, TestMultiThreadEnforce) {
casbin::SyncedEnforcer e(basic_model_path, basic_policy_path);
using namespace std::literals::chrono_literals;
std::chrono::duration<int64_t, std::nano> t = 5ms;
e.StartAutoLoadPolicy(t);
EXPECT_EQ(e.IsAutoLoadingRunning(), true);
for (int i = 0; i < 100; ++i) {
std::thread t1([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"alice", "data1", "read"}), true); });
std::thread t2([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"alice", "data1", "write"}), false); });
std::thread t3([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"alice", "data2", "read"}), false); });
std::thread t4([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"alice", "data2", "write"}), false); });
std::thread t5([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"bob", "data1", "read"}), false); });
std::thread t6([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"bob", "data1", "write"}), false); });
std::thread t7([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"bob", "data2", "read"}), false); });
std::thread t8([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"bob", "data2", "write"}), true); });
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
t7.join();
t8.join();
}
for (int i = 0; i < 100; ++i) {
std::thread t1([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"alice", "data1", "read"}), true); });
std::thread t2([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"alice", "data1", "write"}), false); });
std::thread t3([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"alice", "data2", "read"}), false); });
std::thread t4([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"alice", "data2", "write"}), false); });
std::thread t5([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"bob", "data1", "read"}), false); });
std::thread t6([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"bob", "data1", "write"}), false); });
std::thread t7([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"bob", "data2", "read"}), false); });
std::thread t8([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"bob", "data2", "write"}), true); });
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
t7.join();
t8.join();
}
for (int i = 0; i < 100; ++i) {
std::thread t1([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "alice"}, {"obj", "data1"}, {"act", "read"}}), true); });
std::thread t2([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "alice"}, {"obj", "data1"}, {"act", "write"}}), false); });
std::thread t3([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "alice"}, {"obj", "data2"}, {"act", "read"}}), false); });
std::thread t4([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "alice"}, {"obj", "data2"}, {"act", "write"}}), false); });
std::thread t5([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "bob"}, {"obj", "data1"}, {"act", "read"}}), false); });
std::thread t6([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "bob"}, {"obj", "data1"}, {"act", "write"}}), false); });
std::thread t7([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "bob"}, {"obj", "data2"}, {"act", "read"}}), false); });
std::thread t8([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "bob"}, {"obj", "data2"}, {"act", "write"}}), true); });
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
t7.join();
t8.join();
}
std::mutex mtx; // for evaluator
for (int i = 0; i < 100; ++i) {
std::thread t1([&] {
std::shared_ptr<casbin::IEvaluator> evaluator;
{
std::scoped_lock lock(mtx);
evaluator = InitializeParams<casbin::ExprtkEvaluator>("alice", "data1", "read");
}
ASSERT_EQ(e.Enforce(evaluator), true);
});
std::thread t2([&] {
std::shared_ptr<casbin::IEvaluator> evaluator;
{
std::scoped_lock lock(mtx);
evaluator = InitializeParams<casbin::ExprtkEvaluator>("alice", "data1", "write");
}
ASSERT_EQ(e.Enforce(evaluator), false);
});
std::thread t3([&] {
std::shared_ptr<casbin::IEvaluator> evaluator;
{
std::scoped_lock lock(mtx);
evaluator = InitializeParams<casbin::ExprtkEvaluator>("alice", "data2", "read");
}
ASSERT_EQ(e.Enforce(evaluator), false);
});
std::thread t4([&] {
std::shared_ptr<casbin::IEvaluator> evaluator;
{
std::scoped_lock lock(mtx);
evaluator = InitializeParams<casbin::ExprtkEvaluator>("alice", "data2", "write");
}
ASSERT_EQ(e.Enforce(evaluator), false);
});
std::thread t5([&] {
std::shared_ptr<casbin::IEvaluator> evaluator;
{
std::scoped_lock lock(mtx);
evaluator = InitializeParams<casbin::ExprtkEvaluator>("bob", "data1", "read");
}
ASSERT_EQ(e.Enforce(evaluator), false);
});
std::thread t6([&] {
std::shared_ptr<casbin::IEvaluator> evaluator;
{
std::scoped_lock lock(mtx);
evaluator = InitializeParams<casbin::ExprtkEvaluator>("bob", "data1", "write");
}
ASSERT_EQ(e.Enforce(evaluator), false);
});
std::thread t7([&] {
std::shared_ptr<casbin::IEvaluator> evaluator;
{
std::scoped_lock lock(mtx);
evaluator = InitializeParams<casbin::ExprtkEvaluator>("bob", "data2", "read");
}
ASSERT_EQ(e.Enforce(evaluator), false);
});
std::thread t8([&] {
std::shared_ptr<casbin::IEvaluator> evaluator;
{
std::scoped_lock lock(mtx);
evaluator = InitializeParams<casbin::ExprtkEvaluator>("bob", "data2", "write");
}
ASSERT_EQ(e.Enforce(evaluator), true);
});
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
t7.join();
t8.join();
}
e.StopAutoLoadPolicy();
std::this_thread::sleep_for(10ms);
EXPECT_EQ(e.IsAutoLoadingRunning(), false);
}
TEST(TestEnforcerSynced, TestMultiThreadBatchEnforce) {
casbin::SyncedEnforcer e(basic_model_path, basic_policy_path);
using namespace std::literals::chrono_literals;
std::chrono::duration<int64_t, std::nano> t = 5ms;
e.StartAutoLoadPolicy(t);
EXPECT_EQ(e.IsAutoLoadingRunning(), true);
for (int i = 0; i < 100; ++i) {
std::thread t1([&] {
std::vector<bool> expect_result{true, false, false, false};
ASSERT_EQ(e.BatchEnforce({{"alice", "data1", "read"}, {"alice", "data1", "write"}, {"alice", "data2", "read"}, {"alice", "data2", "write"}}), expect_result);
});
std::thread t2([&] {
std::vector<bool> expect_result{false, false, false, true};
ASSERT_EQ(e.BatchEnforce({{"bob", "data1", "read"}, {"bob", "data1", "write"}, {"bob", "data2", "read"}, {"bob", "data2", "write"}}), expect_result);
});
std::thread t3([&] {
std::vector<bool> expect_result{true, false, false, false};
ASSERT_EQ(e.BatchEnforce({{"alice", "data1", "read"}, {"alice", "data1", "write"}, {"bob", "data1", "read"}, {"bob", "data1", "write"}}), expect_result);
});
std::thread t4([&] {
std::vector<bool> expect_result{false, false, false, true};
ASSERT_EQ(e.BatchEnforce({{"alice", "data2", "read"}, {"alice", "data2", "write"}, {"bob", "data2", "read"}, {"bob", "data2", "write"}}), expect_result);
});
t1.join();
t2.join();
t3.join();
t4.join();
}
e.StopAutoLoadPolicy();
std::this_thread::sleep_for(10ms);
EXPECT_EQ(e.IsAutoLoadingRunning(), false);
}
void testSyncedEnforcerGetPolicy(casbin::SyncedEnforcer& e, PoliciesVector expected) {
auto myRes = e.GetPolicy();
PoliciesVector actual;
for (auto it = myRes.begin(); it != myRes.end(); ++it) {
actual.push_back(*it);
}
std::sort(actual.begin(), actual.end());
std::sort(expected.begin(), expected.end());
ASSERT_EQ(expected, actual);
}
TEST(TestSyncedEnforcer, GetPolicy) {
casbin::SyncedEnforcer e(basic_model_path, basic_policy_path);
PoliciesVector expected_policy = {
{"alice", "data1", "read"},
{"bob", "data2", "write"},
};
testSyncedEnforcerGetPolicy(e, expected_policy);
}
} // namespace