Skip to content

Commit

Permalink
feat: add pycasbin adopter and test. (#164)
Browse files Browse the repository at this point in the history
* fix: fix FileAdapter::SavePolicy

Signed-off-by: stonex <1479765922@qq.com>

* feat: add pycasbin::Adopter and test

Signed-off-by: stonex <1479765922@qq.com>
  • Loading branch information
sheny1xuan authored Nov 13, 2021
1 parent 7790669 commit 4371da5
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 6 deletions.
1 change: 1 addition & 0 deletions bindings/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ set(SOURCES
py_model.cpp
py_config.cpp
py_synced_enforcer.cpp
py_adapter.cpp
)

set(HEADERS
Expand Down
1 change: 1 addition & 0 deletions bindings/python/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ PYBIND11_MODULE(pycasbin, m) {
bindPyModel(m);
bindPyConfig(m);
bindPySyncedEnforcer(m);
bindPyAdapter(m);

m.attr("__version__") = PY_CASBIN_VERSION;
}
70 changes: 70 additions & 0 deletions bindings/python/py_adapter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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 <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <casbin/casbin.h>

#include "py_casbin.h"

namespace py = pybind11;

void bindPyBaseAdapter(py::module& m) {
// Base Adapter use shared_ptr to manage
// must expose this interface for enforcer build
py::class_<casbin::Adapter, std::shared_ptr<casbin::Adapter>>(m, "Adapter")
.def("LoadPolicy", &casbin::Adapter::LoadPolicy, "LoadPolicy loads all policy rules from the storage.")
.def("SavePolicy", &casbin::Adapter::SavePolicy, "SavePolicy saves all policy rules to the storage.")
.def("AddPolicy", &casbin::Adapter::AddPolicy, "AddPolicy adds a policy rule to the storage.")
.def("RemovePolicy", &casbin::Adapter::RemovePolicy, "RemovePolicy removes a policy rule from the storage.")
.def("RemoveFilteredPolicy", &casbin::Adapter::RemoveFilteredPolicy, "RemoveFilteredPolicy removes policy rules that match the filter from the storage.")
.def("IsFiltered", &casbin::Adapter::IsFiltered, "IsFiltered returns true if the loaded policy has been filtered.");
}

void bindPyBatchAdapter(py::module &m) {
py::class_<casbin::BatchAdapter, casbin::Adapter, std::shared_ptr<casbin::BatchAdapter>>(m, "BatchAdapter")
.def("AddPolicies", &casbin::BatchAdapter::AddPolicies, "")
.def("RemovePolicies", &casbin::BatchAdapter::RemovePolicies, "");
}

void bindPyFileAdapter(py::module &m) {
// File adapter inhert base adapter and use shared_ptr to manage
py::class_<casbin::FileAdapter, casbin::Adapter, std::shared_ptr<casbin::FileAdapter>>(m, "FileAdapter")
.def(py::init<std::string>(), "")
.def_static("NewFileAdapter", &casbin::FileAdapter::NewFileAdapter, "")
.def("LoadPolicy", &casbin::FileAdapter::LoadPolicy, "LoadPolicy loads all policy rules from the storage.")
.def("SavePolicy", &casbin::FileAdapter::SavePolicy, "SavePolicy saves all policy rules to the storage.")
.def("AddPolicy", &casbin::FileAdapter::AddPolicy, "AddPolicy adds a policy rule to the storage.")
.def("RemovePolicy", &casbin::FileAdapter::RemovePolicy, "RemovePolicy removes a policy rule from the storage.")
.def("RemoveFilteredPolicy", &casbin::FileAdapter::RemoveFilteredPolicy, "RemoveFilteredPolicy removes policy rules that match the filter from the storage.")
.def("IsFiltered", &casbin::FileAdapter::IsFiltered, "IsFiltered returns true if the loaded policy has been filtered.");
}

void bindPyBatchFileAdapter(py::module &m) {
// Batch Adapter is virtual interface, maybe don't expose its' interface is ok.
py::class_<casbin::BatchFileAdapter, casbin::BatchAdapter, casbin::FileAdapter, std::shared_ptr<casbin::BatchFileAdapter>>(m, "BatchFileAdapter")
.def(py::init<std::string>(), "")
.def_static("NewBatchFileAdapter", &casbin::BatchFileAdapter::NewBatchFileAdapter, "")
.def("AddPolicies", &casbin::BatchFileAdapter::AddPolicies, "")
.def("RemovePolicies", &casbin::BatchFileAdapter::RemovePolicies, "");
}

void bindPyAdapter(py::module& m) {
bindPyBaseAdapter(m);
bindPyBatchAdapter(m);
bindPyFileAdapter(m);
bindPyBatchFileAdapter(m);
}
1 change: 1 addition & 0 deletions bindings/python/py_casbin.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ void bindABACData(py::module &m);
void bindPyModel(py::module &m);
void bindPyConfig(py::module &m);
void bindPySyncedEnforcer(py::module& m);
void bindPyAdapter(py::module& m);
4 changes: 4 additions & 0 deletions casbin/persist/file_adapter/batch_file_adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ namespace casbin {
BatchFileAdapter :: BatchFileAdapter(std::string file_path): FileAdapter(file_path) {
}

std::shared_ptr<BatchFileAdapter> BatchFileAdapter :: NewBatchFileAdapter(std::string file_path) {
return std::make_shared<BatchFileAdapter>(file_path);
}

void BatchFileAdapter :: AddPolicies(std::string sec, std::string p_type, std::vector<std::vector<std::string>> rules) {
throw UnsupportedOperationException("not implemented hello");
}
Expand Down
2 changes: 2 additions & 0 deletions casbin/persist/file_adapter/batch_file_adapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class BatchFileAdapter: public BatchAdapter, public FileAdapter {
// NewAdapter is the constructor for Adapter.
BatchFileAdapter(std::string file_path);

static std::shared_ptr<BatchFileAdapter> NewBatchFileAdapter(std::string file_path);

void AddPolicies(std::string sec, std::string p_type, std::vector<std::vector<std::string>> rules);

void RemovePolicies(std::string sec, std::string p_type, std::vector<std::vector<std::string>> rules);
Expand Down
16 changes: 12 additions & 4 deletions casbin/persist/file_adapter/file_adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ FileAdapter :: FileAdapter(std::string file_path) {
this->filtered = false;
}

std::shared_ptr<casbin::FileAdapter> FileAdapter::NewFileAdapter(std::string file_path) {
return std::make_shared<FileAdapter>(file_path);
}

// LoadPolicy loads all policy rules from the storage.
void FileAdapter :: LoadPolicy(const std::shared_ptr<Model>& model) {
if (this->file_path == "")
Expand All @@ -36,15 +40,15 @@ void FileAdapter :: SavePolicy(const std::shared_ptr<Model>& model) {

std::string tmp;

for (std::unordered_map<std::string, std::shared_ptr<Assertion>> :: iterator it = model->m["p"].assertion_map.begin() ; it != model->m["p"].assertion_map.begin() ; it++){
for (std::unordered_map<std::string, std::shared_ptr<Assertion>> :: iterator it = model->m["p"].assertion_map.begin() ; it != model->m["p"].assertion_map.end() ; it++){
for (int i = 0 ; i < it->second->policy.size() ; i++){
tmp += it->first + ", ";
tmp += ArrayToString(it->second->policy[i]);
tmp += "\n";
}
}

for (std::unordered_map <std::string, std::shared_ptr<Assertion>> :: iterator it = model->m["g"].assertion_map.begin() ; it != model->m["g"].assertion_map.begin() ; it++){
for (std::unordered_map <std::string, std::shared_ptr<Assertion>> :: iterator it = model->m["g"].assertion_map.begin() ; it != model->m["g"].assertion_map.end() ; it++){
for (int i = 0 ; i < it->second->policy.size() ; i++){
tmp += it->first + ", ";
tmp += ArrayToString(it->second->policy[i]);
Expand Down Expand Up @@ -74,14 +78,18 @@ void FileAdapter :: LoadPolicyFile(const std::shared_ptr<Model>& model, std::fun

void FileAdapter :: SavePolicyFile(std::string text) {
std::ofstream out_file;
out_file.open(this->file_path, std::ios::out);

try {
out_file.open(this->file_path, std::ios::out);
} catch (const std::ifstream::failure e) {
throw IOException("Cannot open file.");
}

out_file<<text;
if (out_file.is_open() == false) {
throw IOException("Don't exit adapter file");
}

out_file << text;

out_file.close();
}
Expand Down
2 changes: 2 additions & 0 deletions casbin/persist/file_adapter/file_adapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class FileAdapter : virtual public Adapter {
// NewAdapter is the constructor for Adapter.
FileAdapter(std::string file_path);

static std::shared_ptr<FileAdapter> NewFileAdapter(std::string file_path);

// LoadPolicy loads all policy rules from the storage.
void LoadPolicy(const std::shared_ptr<Model>& model);

Expand Down
4 changes: 2 additions & 2 deletions casbin/util/array_to_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
namespace casbin {

std::string ArrayToString(const std::vector<std::string>& arr){
std::string res = arr[0];
std::string res = "";
for (const std::string& it : arr)
res += ", " + it;
return res;
return res.substr(2);
}

} // namespace casbin
Expand Down
4 changes: 4 additions & 0 deletions include/casbin/casbin_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,8 @@ namespace casbin {
// NewAdapter is the constructor for Adapter.
FileAdapter(std::string file_path);

static std::shared_ptr<FileAdapter> NewFileAdapter(std::string file_path);

// LoadPolicy loads all policy rules from the storage.
void LoadPolicy(const std::shared_ptr<Model>& model);

Expand Down Expand Up @@ -463,6 +465,8 @@ namespace casbin {
// NewAdapter is the constructor for Adapter.
BatchFileAdapter(std::string file_path);

static std::shared_ptr<BatchFileAdapter> NewBatchFileAdapter(std::string file_path);

void AddPolicies(std::string sec, std::string p_type, std::vector<std::vector<std::string>> rules);

void RemovePolicies(std::string sec, std::string p_type, std::vector<std::vector<std::string>> rules);
Expand Down
2 changes: 2 additions & 0 deletions tests/python/pycasbin_test_suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import test_config
import test_cached_enforcer
import test_synced_enforcer
import test_adapter

def suite():

Expand All @@ -32,6 +33,7 @@ def suite():
suite.addTest(loader.loadTestsFromModule(test_model))
suite.addTest(loader.loadTestsFromModule(test_config))
suite.addTest(loader.loadTestsFromModule(test_cached_enforcer))
suite.addTest(loader.loadTestsFromModule(test_adapter))
#suite.addTest(loader.loadTestsFromModule(test_synced_enforcer))

return suite
Expand Down
57 changes: 57 additions & 0 deletions tests/python/test_adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# 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.

from logging import setLoggerClass
import pycasbin as casbin
from config_path import *
import unittest
import os

class TestAdapter(unittest.TestCase):

def setUp(self):
self.Adapter = None
self.Model = None

def cleanUp(self):
self.Adapter = None
self.Model = None

def FileAdapterInit(self, path):
self.Adapter = casbin.FileAdapter.NewFileAdapter(path)

def ModelInit(self, model_path):
self.Model = casbin.Model.NewModelFromFile(model_path)

def test_NewFileAdapter(self):
self.cleanUp()
self.Adapter = casbin.FileAdapter.NewFileAdapter(basic_policy_path)
self.assertIsNotNone(self.Adapter)

def test_NewBatchFileAdapter(self):
self.cleanUp()
self.Adapter = casbin.BatchFileAdapter.NewBatchFileAdapter(basic_model_path)
self.assertIsNotNone(self.Adapter)

def test_FileAdapterLoadPolicy(self):
self.cleanUp()
self.FileAdapterInit(basic_policy_path)
self.ModelInit(basic_model_path)
self.Adapter.LoadPolicy(self.Model)

FilePolicies = [["alice", "data1", "read"],
["bob", "data2", "write"]]

self.assertTrue(self.Model.HasPolicy('p', 'p', FilePolicies[0]))
self.assertTrue(self.Model.HasPolicy('p', 'p', FilePolicies[1]))
14 changes: 14 additions & 0 deletions tests/python/test_enforcer.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ def initEnforcer(self, model, policy):

def tearDown(self):
self.e = None

def test_FileAdapterEnforcer(self):
self.fileAdapter = casbin.FileAdapter.NewFileAdapter(rbac_with_domains_policy_path)
self.e = casbin.Enforcer(rbac_with_domains_model_path, self.fileAdapter)

self.assertIsNotNone(self.fileAdapter)
self.assertIsNotNone(self.e)

def test_BatchFileAdapterEnforcer(self):
self.fileAdapter = casbin.BatchFileAdapter.NewBatchFileAdapter(rbac_with_domains_policy_path)
self.e = casbin.Enforcer(rbac_with_domains_model_path, self.fileAdapter)

self.assertIsNotNone(self.fileAdapter)
self.assertIsNotNone(self.e)

def test_FourParams(self):
self.initEnforcer(rbac_with_domains_model_path, rbac_with_domains_policy_path)
Expand Down

0 comments on commit 4371da5

Please sign in to comment.