Skip to content

Commit 3332324

Browse files
authored
feat: Added tests for Python Bindings (#133)
* feat: Added CI for python bindings Signed-off-by: Yash Pandey (YP) <[email protected]> * fix: Pybind CI Signed-off-by: Yash Pandey (YP) <[email protected]>
1 parent 6d8baae commit 3332324

File tree

10 files changed

+254
-3
lines changed

10 files changed

+254
-3
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Copyright 2021 The casbin Authors. All Rights Reserved.
2+
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: Python Bindings Test
16+
17+
on: [push, pull_request]
18+
19+
jobs:
20+
benchmark:
21+
name: Python Bindings Test
22+
runs-on: macos-latest
23+
steps:
24+
- name: Checkout
25+
id: checkout
26+
uses: actions/checkout@v2
27+
- name: Configuring CMake files
28+
id: building-files
29+
run: |
30+
mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE:STRING=Release
31+
- name: Building library
32+
id: building-lib
33+
run: |
34+
cd build && cmake --build . --config Release --target all -j 10 --
35+
- name: Installing pycasbin
36+
id: installing-pycasbin
37+
run: |
38+
cd build && sudo cmake --build . --config Release --target install -j 10 --
39+
- name: Run Tests
40+
id: run-tests
41+
run: |
42+
cd tests/python && python3 pycasbin_test_suite.py
43+
- name: Cleanup
44+
id: clean-up
45+
run: |
46+
rm -r build
47+
rm tests/python/pycasbin.so

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ MigrationBackup/
355355
*.iml
356356
.vscode
357357
.DS_Store
358+
*.so
358359

359360
# CMake work directory
360361
cmake-build/

bindings/python/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,7 @@ target_link_libraries(pycasbin
6969
casbin
7070
)
7171

72+
install(
73+
TARGETS pycasbin
74+
DESTINATION ${CMAKE_SOURCE_DIR}/tests/python
75+
)

bindings/python/py_enforcer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ void bindPyEnforcer(py::module& m) {
6161
.def("BuildRoleLinks", &casbin::Enforcer::BuildRoleLinks, "BuildRoleLinks manually rebuild the role inheritance relations.")
6262
.def("BuildIncrementalRoleLinks", &casbin::Enforcer::BuildIncrementalRoleLinks, "BuildIncrementalRoleLinks provides incremental build the role inheritance relations.")
6363
// .def("Enforce", py::overload_cast<casbin::Scope>(&casbin::Enforcer::Enforce), "Enforce decides whether a \"subject\" can access a \"object\" with the operation \"action\", input parameters are usually: (sub, obj, act).")
64-
.def("Enforce", py::overload_cast<const casbin::DataList &>(&casbin::Enforcer::Enforce), "Enforce with a vector param,decides whether a \"subject\" can access a \"object\" with the operation \"action\", input parameters are usually: (sub, obj, act).")
64+
.def("Enforce", py::overload_cast<const casbin::DataVector &>(&casbin::Enforcer::Enforce), "Enforce with a vector param, decides whether a \"subject\" can access a \"object\" with the operation \"action\", input parameters are usually: (sub, obj, act).")
6565
.def("Enforce", py::overload_cast<const casbin::DataMap &>(&casbin::Enforcer::Enforce), "Enforce with a map param, decides whether a \"subject\" can access a \"object\" with the operation \"action\", input parameters are usually: (sub, obj, act).")
6666
// .def("EnforceWithMatcher", py::overload_cast<const std::string &, casbin::Scope>(&casbin::Enforcer::EnforceWithMatcher), "EnforceWithMatcher use a custom matcher to decides whether a \"subject\" can access a \"object\" with the operation \"action\", input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is \"\".")
6767
.def("EnforceWithMatcher", py::overload_cast<const std::string &, const casbin::DataList &>(&casbin::Enforcer::EnforceWithMatcher), "EnforceWithMatcher use a custom matcher to decides whether a \"subject\" can access a \"object\" with the operation \"action\", input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is \"\".")

casbin/enforcer.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,10 @@ bool Enforcer::Enforce(const DataList& params) {
439439
return this->EnforceWithMatcher("", params);
440440
}
441441

442+
bool Enforcer::Enforce(const DataVector& params) {
443+
return this->EnforceWithMatcher("", params);
444+
}
445+
442446
// Enforce with a map param,decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
443447
bool Enforcer::Enforce(const DataMap& params) {
444448
return this->EnforceWithMatcher("", params);
@@ -504,6 +508,61 @@ bool Enforcer::EnforceWithMatcher(const std::string& matcher, const DataList& pa
504508
return result;
505509
}
506510

511+
// EnforceWithMatcher use a custom matcher to decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "".
512+
bool Enforcer::EnforceWithMatcher(const std::string& matcher, const DataVector& params) {
513+
const std::vector<std::string>& r_tokens = m_model->m["r"].assertion_map["r"]->tokens;
514+
515+
size_t r_cnt = r_tokens.size();
516+
size_t cnt = params.size();
517+
518+
if (cnt != r_cnt)
519+
return false;
520+
521+
Scope scope = InitializeScope();
522+
PushObject(scope, "r");
523+
524+
size_t i = 0;
525+
526+
for(const auto& param : params) {
527+
if(const auto string_param = std::get_if<std::string>(&param)) {
528+
PushStringPropToObject(scope, "r", *string_param, r_tokens[i].substr(2, r_tokens[i].size() - 2));
529+
}
530+
else if(const auto abac_param = std::get_if<std::shared_ptr<ABACData>>(&param)) {
531+
auto data_ptr = *abac_param;
532+
std::string token_name = r_tokens[i].substr(2, r_tokens[i].size() - 2);
533+
534+
PushObjectPropToObject(scope, "r", token_name);
535+
536+
for(auto [attrib_name, attrib_value] : data_ptr->GetAttributes()) {
537+
538+
if(const auto string_value = std::get_if<std::string>(&attrib_value))
539+
PushStringPropToObject(scope, token_name, *string_value, attrib_name);
540+
541+
else if(const auto int_value = std::get_if<int32_t>(&attrib_value))
542+
PushIntPropToObject(scope, token_name, *int_value, attrib_name);
543+
544+
else if(const auto float_value = std::get_if<float>(&attrib_value))
545+
PushFloatPropToObject(scope, token_name, *float_value, attrib_name);
546+
547+
else if(const auto double_value = std::get_if<double>(&attrib_value))
548+
PushDoublePropToObject(scope, token_name, *double_value, attrib_name);
549+
550+
else
551+
throw CasbinEnforcerException("Not a valid type");
552+
}
553+
}
554+
++i;
555+
}
556+
557+
// for (size_t i = 0; i < cnt; i++) {
558+
// PushStringPropToObject(scope, "r", params[i], r_tokens[i].substr(2, r_tokens[i].size() - 2));
559+
// }
560+
561+
bool result = m_enforce(matcher, scope);
562+
DeinitializeScope(scope);
563+
return result;
564+
}
565+
507566
// EnforceWithMatcher use a custom matcher to decides whether a "subject" can access a "object"
508567
// with the operation "action", input parameters are usually: (matcher, sub, obj, act),
509568
// use model matcher by default when matcher is "".

casbin/enforcer.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,19 @@ class Enforcer : public IEnforcer {
155155
void BuildIncrementalRoleLinks(policy_op op, const std::string& p_type, const std::vector<std::vector<std::string>>& rules);
156156
// Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
157157
bool Enforce(Scope scope);
158-
// Enforce with a vector param,decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
158+
// Enforce with a list param, decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
159159
bool Enforce(const DataList& params);
160+
// Enforce with a vector param, decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
161+
bool Enforce(const DataVector& params);
160162
// Enforce with a map param,decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
161163
bool Enforce(const DataMap& params);
162164
// EnforceWithMatcher use a custom matcher to decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "".
163165
bool EnforceWithMatcher(const std::string& matcher, Scope scope);
164166
// EnforceWithMatcher use a custom matcher to decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "".
165167
bool EnforceWithMatcher(const std::string& matcher, const DataList& params);
166168
// EnforceWithMatcher use a custom matcher to decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "".
169+
bool EnforceWithMatcher(const std::string& matcher, const DataVector& params);
170+
// EnforceWithMatcher use a custom matcher to decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "".
167171
bool EnforceWithMatcher(const std::string& matcher, const DataMap& params);
168172
// BatchEnforce enforce in batches
169173
std::vector<bool> BatchEnforce(const std::initializer_list<DataList>& requests);
@@ -190,7 +194,7 @@ class Enforcer : public IEnforcer {
190194
bool HasPolicy(const std::vector<std::string>& params);
191195
bool HasNamedPolicy(const std::string& p_type, const std::vector<std::string>& params);
192196
bool AddPolicy(const std::vector<std::string>& params);
193-
bool AddPolicies(const std::vector<std::vector<std::string>>& rules);
197+
bool AddPolicies(const std::vector<std::vector<std::string>>& rules);
194198
bool AddNamedPolicy(const std::string& p_type, const std::vector<std::string>& params);
195199
bool AddNamedPolicies(const std::string& p_type, const std::vector<std::vector<std::string>>& rules);
196200
bool RemovePolicy(const std::vector<std::string>& params);

tests/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,7 @@ endif()
5252
if(CASBIN_BUILD_BENCHMARK)
5353
add_subdirectory(benchmarks)
5454
endif()
55+
56+
# if(CASBIN_BUILD_PYTHON_BINDINGS)
57+
# add_subdirectory(python)
58+
# endif()

tests/python/config_path.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright 2021 The casbin Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
relative_path = '../../'
16+
basic_model_path = relative_path + 'examples/basic_model.conf'
17+
basic_policy_path = relative_path + 'examples/basic_policy.csv'
18+
rbac_model_path = relative_path + 'examples/rbac_model.conf'
19+
rbac_policy_path = relative_path + 'examples/rbac_policy.csv'
20+
rbac_with_resource_roles_model_path = relative_path + 'examples/rbac_with_resource_roles_model.conf'
21+
rbac_with_resource_roles_policy_path = relative_path + 'examples/rbac_with_resource_roles_policy.csv'
22+
rbac_with_domains_model_path = relative_path + 'examples/rbac_with_domains_model.conf'
23+
rbac_with_domains_policy_path = relative_path + 'examples/rbac_with_domains_policy.csv'
24+
keymatch_model_path = relative_path + 'examples/keymatch_model.conf'
25+
keymatch_policy_path = relative_path + 'examples/keymatch_policy.csv'
26+
rbac_with_deny_model_path = relative_path + 'examples/rbac_with_deny_model.conf'
27+
rbac_with_deny_policy_path = relative_path + 'examples/rbac_with_deny_policy.csv'
28+
priority_model_path = relative_path + 'examples/priority_model.conf'
29+
priority_policy_path = relative_path + 'examples/priority_policy.csv'
30+
basic_model_without_spaces_path = relative_path + 'examples/basic_model_without_spaces.conf'
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright 2021 The casbin Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import unittest
16+
import sys
17+
import os
18+
import pycasbin
19+
import test_enforcer
20+
21+
def suite():
22+
23+
# top level directory cached on loader instance
24+
suite = unittest.TestSuite()
25+
loader = unittest.TestLoader()
26+
27+
suite.addTest(loader.loadTestsFromModule(test_enforcer))
28+
return suite
29+
30+
31+
if __name__ == '__main__':
32+
runner = unittest.TextTestRunner(verbosity=2)
33+
test_suite = suite()
34+
result = runner.run(test_suite)
35+
if result.wasSuccessful() == False:
36+
sys.exit(1)
37+
sys.exit(0)

tests/python/test_enforcer.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Copyright 2021 The casbin Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import pycasbin as casbin
16+
from config_path import *
17+
import unittest
18+
19+
class TestEnforcer(unittest.TestCase):
20+
def setUp(self):
21+
self.initEnforcer(rbac_with_domains_model_path, rbac_with_domains_policy_path)
22+
23+
def initEnforcer(self, model, policy):
24+
self.current_model = model
25+
self.current_policy = policy
26+
self.e = casbin.Enforcer(self.current_model, self.current_policy)
27+
28+
def tearDown(self):
29+
self.e = None
30+
31+
def test_FourParams(self):
32+
self.initEnforcer(rbac_with_domains_model_path, rbac_with_domains_policy_path)
33+
34+
self.assertEqual(self.e.Enforce(['alice', 'domain1', 'data1', 'read']), True)
35+
self.assertEqual(self.e.Enforce(['alice', 'domain1', 'data1', 'write']), True)
36+
self.assertEqual(self.e.Enforce(['alice', 'domain1', 'data2', 'read']), False)
37+
self.assertEqual(self.e.Enforce(['alice', 'domain1', 'data2', 'write']), False)
38+
self.assertEqual(self.e.Enforce(['bob', 'domain2', 'data1', 'read']), False)
39+
self.assertEqual(self.e.Enforce(['bob', 'domain2', 'data1', 'write']), False)
40+
self.assertEqual(self.e.Enforce(['bob', 'domain2', 'data2', 'read']), True)
41+
self.assertEqual(self.e.Enforce(['bob', 'domain2', 'data2', 'write']), True)
42+
43+
def test_ThreeParams(self):
44+
self.initEnforcer(basic_model_without_spaces_path, basic_policy_path)
45+
46+
self.assertEqual(self.e.Enforce([ 'alice', 'data1', 'read' ]), True)
47+
self.assertEqual(self.e.Enforce([ 'alice', 'data1', 'write' ]), False)
48+
self.assertEqual(self.e.Enforce([ 'alice', 'data2', 'read' ]), False)
49+
self.assertEqual(self.e.Enforce([ 'alice', 'data2', 'write' ]), False)
50+
self.assertEqual(self.e.Enforce([ 'bob', 'data1', 'read' ]), False)
51+
self.assertEqual(self.e.Enforce([ 'bob', 'data1', 'write' ]), False)
52+
self.assertEqual(self.e.Enforce([ 'bob', 'data2', 'read' ]), False)
53+
self.assertEqual(self.e.Enforce([ 'bob', 'data2', 'write' ]), True)
54+
55+
def test_VectorParams(self):
56+
self.initEnforcer(basic_model_without_spaces_path, basic_policy_path)
57+
58+
self.assertEqual(self.e.Enforce([ 'alice', 'data1', 'read' ]), True)
59+
self.assertEqual(self.e.Enforce([ 'alice', 'data1', 'write' ]), False)
60+
self.assertEqual(self.e.Enforce([ 'alice', 'data2', 'read' ]), False)
61+
self.assertEqual(self.e.Enforce([ 'alice', 'data2', 'write' ]), False)
62+
self.assertEqual(self.e.Enforce([ 'bob', 'data1', 'read' ]), False)
63+
self.assertEqual(self.e.Enforce([ 'bob', 'data1', 'write' ]), False)
64+
self.assertEqual(self.e.Enforce([ 'bob', 'data2', 'read' ]), False)
65+
self.assertEqual(self.e.Enforce([ 'bob', 'data2', 'write' ]), True)

0 commit comments

Comments
 (0)