Skip to content

Commit 285510d

Browse files
authored
feat: support PyMongo Async API
1 parent 9d1d8d7 commit 285510d

8 files changed

Lines changed: 394 additions & 45 deletions

File tree

casbin_pymongo_adapter/_rule.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
class CasbinRule:
2+
"""
3+
CasbinRule model
4+
"""
5+
6+
def __init__(
7+
self, ptype=None, v0=None, v1=None, v2=None, v3=None, v4=None, v5=None
8+
):
9+
self.ptype = ptype
10+
self.v0 = v0
11+
self.v1 = v1
12+
self.v2 = v2
13+
self.v3 = v3
14+
self.v4 = v4
15+
self.v5 = v5
16+
17+
def dict(self):
18+
d = {"ptype": self.ptype}
19+
20+
for value in dir(self):
21+
if (
22+
getattr(self, value) is not None
23+
and value.startswith("v")
24+
and value[1:].isnumeric()
25+
):
26+
d[value] = getattr(self, value)
27+
28+
return d
29+
30+
def __str__(self):
31+
return ", ".join(self.dict().values())
32+
33+
def __repr__(self):
34+
return '<CasbinRule :"{}">'.format(str(self))

casbin_pymongo_adapter/adapter.py

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,7 @@
11
from casbin import persist
22
from pymongo import MongoClient
33

4-
5-
class CasbinRule:
6-
"""
7-
CasbinRule model
8-
"""
9-
10-
def __init__(
11-
self, ptype=None, v0=None, v1=None, v2=None, v3=None, v4=None, v5=None
12-
):
13-
self.ptype = ptype
14-
self.v0 = v0
15-
self.v1 = v1
16-
self.v2 = v2
17-
self.v3 = v3
18-
self.v4 = v4
19-
self.v5 = v5
20-
21-
def dict(self):
22-
d = {"ptype": self.ptype}
23-
24-
for value in dir(self):
25-
if (
26-
getattr(self, value) is not None
27-
and value.startswith("v")
28-
and value[1:].isnumeric()
29-
):
30-
d[value] = getattr(self, value)
31-
32-
return d
33-
34-
def __str__(self):
35-
return ", ".join(self.dict().values())
36-
37-
def __repr__(self):
38-
return '<CasbinRule :"{}">'.format(str(self))
4+
from ._rule import CasbinRule
395

406

417
class Adapter(persist.Adapter):

casbin_pymongo_adapter/asynchronous/__init__.py

Whitespace-only changes.
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
from casbin import persist
2+
from casbin.persist.adapters.asyncio.adapter import AsyncAdapter
3+
from pymongo import AsyncMongoClient
4+
5+
from .._rule import CasbinRule
6+
7+
8+
class Adapter(AsyncAdapter):
9+
"""the interface for Casbin adapters."""
10+
11+
def __init__(self, uri, dbname, collection="casbin_rule"):
12+
"""Create an adapter for Mongodb
13+
14+
Args:
15+
uri (str): This should be the same requiement as pymongo Client's 'uri' parameter.
16+
See https://pymongo.readthedocs.io/en/stable/api/pymongo/mongo_client.html#pymongo.mongo_client.MongoClient.
17+
dbname (str): Database to store policy.
18+
collection (str, optional): Collection of the choosen database. Defaults to "casbin_rule".
19+
"""
20+
client = AsyncMongoClient(uri)
21+
db = client[dbname]
22+
self._collection = db[collection]
23+
24+
async def load_policy(self, model):
25+
"""Implementing add Interface for casbin. Load all policy rules from mongodb
26+
27+
Args:
28+
model (CasbinRule): CasbinRule object
29+
"""
30+
31+
async for line in self._collection.find():
32+
if "ptype" not in line:
33+
continue
34+
rule = CasbinRule(line["ptype"])
35+
for key, value in line.items():
36+
setattr(rule, key, value)
37+
38+
persist.load_policy_line(str(rule), model)
39+
40+
async def _save_policy_line(self, ptype, rule):
41+
line = CasbinRule(ptype=ptype)
42+
for index, value in enumerate(rule):
43+
setattr(line, f"v{index}", value)
44+
await self._collection.insert_one(line.dict())
45+
46+
async def _delete_policy_lines(self, ptype, rule):
47+
line = CasbinRule(ptype=ptype)
48+
for index, value in enumerate(rule):
49+
setattr(line, f"v{index}", value)
50+
51+
# if rule is empty, do nothing
52+
# else find all given rules and delete them
53+
if len(line.dict()) == 0:
54+
return 0
55+
else:
56+
line_dict = line.dict()
57+
line_dict_keys_len = len(line_dict)
58+
to_delete = [
59+
result["_id"]
60+
async for result in self._collection.find(line_dict)
61+
if line_dict_keys_len == len(result.keys()) - 1
62+
]
63+
results = await self._collection.delete_many({"_id": {"$in": to_delete}})
64+
return results.deleted_count
65+
66+
async def save_policy(self, model) -> bool:
67+
"""Implement add Interface for casbin. Save the policy in mongodb
68+
69+
Args:
70+
model (Class Model): Casbin Model which loads from .conf file usually.
71+
72+
Returns:
73+
bool: True if succeed
74+
"""
75+
for sec in ["p", "g"]:
76+
if sec not in model.model.keys():
77+
continue
78+
for ptype, ast in model.model[sec].items():
79+
for rule in ast.policy:
80+
await self._save_policy_line(ptype, rule)
81+
return True
82+
83+
async def add_policy(self, sec, ptype, rule):
84+
"""Add policy rules to mongodb
85+
86+
Args:
87+
sec (str): Section name, 'g' or 'p'
88+
ptype (str): Policy type, 'g', 'g2', 'p', etc.
89+
rule (CasbinRule): Casbin rule will be added
90+
91+
Returns:
92+
bool: True if succeed else False
93+
"""
94+
await self._save_policy_line(ptype, rule)
95+
return True
96+
97+
async def remove_policy(self, sec, ptype, rule):
98+
"""Remove policy rules in mongodb(rules duplicate are also removed)
99+
100+
Args:
101+
ptype (str): Policy type, 'g', 'g2', 'p', etc.
102+
rule (CasbinRule): Casbin rule if it is exactly same as will be removed.
103+
104+
Returns:
105+
Number: Number of policies be removed
106+
"""
107+
deleted_count = await self._delete_policy_lines(ptype, rule)
108+
return deleted_count > 0
109+
110+
async def remove_filtered_policy(self, sec, ptype, field_index, *field_values):
111+
"""Remove policy rules taht match the filter from the storage.
112+
This is part of the Auto-Save feature.
113+
114+
Args:
115+
ptype (str): Policy type, 'g', 'g2', 'p', etc.
116+
rule (CasbinRule): Casbin rule will be removed
117+
field_index (int): The policy index at which the filed_values begins filtering. Its range is [0, 5]
118+
field_values(List[str]): A list of rules to filter policy which starts from
119+
120+
Returns:
121+
bool: True if succeed else False
122+
"""
123+
if not (0 <= field_index <= 5):
124+
return False
125+
if not (1 <= field_index + len(field_values) <= 6):
126+
return False
127+
query = {
128+
f"v{index + field_index}": value
129+
for index, value in enumerate(field_values)
130+
if value != ""
131+
}
132+
query["ptype"] = ptype
133+
results = await self._collection.delete_many(query)
134+
return results.deleted_count > 0

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
casbin>=0.8.4
2-
pymongo>=3.10.1
2+
pymongo>=4.13.0

0 commit comments

Comments
 (0)