Skip to content

Commit

Permalink
add: policy_fast.py, model_fast.py, and their tests
Browse files Browse the repository at this point in the history
Signed-off-by: terry-xuan-gao <[email protected]>
  • Loading branch information
terry-xuan-gao committed Feb 5, 2023
1 parent 7216844 commit a7ed0bb
Show file tree
Hide file tree
Showing 12 changed files with 327 additions and 258 deletions.
4 changes: 2 additions & 2 deletions casbin/core_enforcer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from typing import Sequence

from casbin.effect import Effector, get_effector, effect_to_bool
from casbin.model import Model, FastModel, FunctionMap, filter_policy
from casbin.model import Model, FastModel, FunctionMap, fast_policy_filter
from casbin.persist import Adapter
from casbin.persist.adapters import FileAdapter
from casbin.rbac import default_role_manager
Expand Down Expand Up @@ -333,7 +333,7 @@ def enforce(self, *rvals):
result, _ = self.enforce_ex(*rvals)
else:
keys = [rvals[x] for x in self._cache_key_order]
with filter_policy(self.model.model["p"]["p"].policy, *keys):
with fast_policy_filter(self.model.model["p"]["p"].policy, *keys):
result, _ = self.enforce_ex(*rvals)

return result
Expand Down
6 changes: 4 additions & 2 deletions casbin/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
# limitations under the License.

from .assertion import Assertion
from .model import Model, FastModel
from .policy import Policy, FilterablePolicy, filter_policy
from .model import Model
from .model_fast import FastModel
from .policy import Policy
from .policy_fast import FastPolicy, fast_policy_filter
from .function import FunctionMap
22 changes: 2 additions & 20 deletions casbin/model/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

from . import Assertion
from casbin import util, config
from .policy import Policy, FilterablePolicy
from typing import Any, Sequence
from .policy import Policy


DEFAULT_DOMAIN = ""
DEFAULT_SEPARATOR = "::"
Expand Down Expand Up @@ -208,21 +208,3 @@ def write_string(sec):
s[-1] = s[-1].strip()

return "".join(s)


class FastModel(Model):
_cache_key_order: Sequence[int]

def __init__(self, cache_key_order: Sequence[int]) -> None:
super().__init__()
self._cache_key_order = cache_key_order

def add_def(self, sec: str, key: str, value: Any) -> None:
super().add_def(sec, key, value)
if sec == "p" and key == "p":
self.model[sec][key].policy = FilterablePolicy(self._cache_key_order)

def clear_policy(self) -> None:
"""clears all current policy."""
super().clear_policy()
self.model["p"]["p"].policy = FilterablePolicy(self._cache_key_order)
35 changes: 35 additions & 0 deletions casbin/model/model_fast.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# 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 .policy_fast import FastPolicy
from typing import Any, Sequence
from .model import Model


class FastModel(Model):
_cache_key_order: Sequence[int]

def __init__(self, cache_key_order: Sequence[int]) -> None:
super().__init__()
self._cache_key_order = cache_key_order

def add_def(self, sec: str, key: str, value: Any) -> None:
super().add_def(sec, key, value)
if sec == "p" and key == "p":
self.model[sec][key].policy = FastPolicy(self._cache_key_order)

def clear_policy(self) -> None:
"""clears all current policy."""
super().clear_policy()
self.model["p"]["p"].policy = FastPolicy(self._cache_key_order)
88 changes: 0 additions & 88 deletions casbin/model/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@

import logging

from contextlib import contextmanager
from typing import Any, Container, Dict, Iterable, Iterator, Optional, Sequence, Set, cast

DEFAULT_SEP = ","


Expand Down Expand Up @@ -292,88 +289,3 @@ def get_values_for_field_in_policy(self, sec, ptype, field_index):
values.append(value)

return values


def in_cache(cache: Dict[str, Any], keys: Sequence[str]) -> Optional[Set[Sequence[str]]]:
if keys[0] in cache:
if len(keys) > 1:
return in_cache(cache[keys[-0]], keys[1:])
return cast(Set[Sequence[str]], cache[keys[0]])
else:
return None


class FilterablePolicy(Container[Sequence[str]]):
_cache: Dict[str, Any]
_current_filter: Optional[Set[Sequence[str]]]
_cache_key_order: Sequence[int]

def __init__(self, cache_key_order: Sequence[int]) -> None:
self._cache = {}
self._current_filter = None
self._cache_key_order = cache_key_order

def __iter__(self) -> Iterator[Sequence[str]]:
yield from self.__get_policy()

def __len__(self) -> int:
return len(list(self.__get_policy()))

def __contains__(self, item: object) -> bool:
if not isinstance(item, (list, tuple)) or len(self._cache_key_order) >= len(item):
return False
keys = [item[x] for x in self._cache_key_order]
exists = in_cache(self._cache, keys)
if not exists:
return False
return tuple(item) in exists

def __getitem__(self, item: int) -> Sequence[str]:
for i, entry in enumerate(self):
if i == item:
return entry
raise KeyError("No such value exists")

def append(self, item: Sequence[str]) -> None:
cache = self._cache
keys = [item[x] for x in self._cache_key_order]

for key in keys[:-1]:
if key not in cache:
cache[key] = dict()
cache = cache[key]
if keys[-1] not in cache:
cache[keys[-1]] = set()

cache[keys[-1]].add(tuple(item))

def remove(self, policy: Sequence[str]) -> bool:
keys = [policy[x] for x in self._cache_key_order]
exists = in_cache(self._cache, keys)
if not exists:
return True

exists.remove(tuple(policy))
return True

def __get_policy(self) -> Iterable[Sequence[str]]:
if self._current_filter is not None:
return (list(x) for x in self._current_filter)
else:
return (list(v2) for v in self._cache.values() for v1 in v.values() for v2 in v1)

def apply_filter(self, *keys: str) -> None:
value = in_cache(self._cache, keys)
self._current_filter = value or set()

def clear_filter(self) -> None:
self._current_filter = None


@contextmanager
def filter_policy(policy: FilterablePolicy, *keys: str) -> Iterator[None]:
try:
policy.apply_filter(*keys)
yield
finally:
policy.clear_filter()
101 changes: 101 additions & 0 deletions casbin/model/policy_fast.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# 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 contextlib import contextmanager
from typing import Any, Container, Dict, Iterable, Iterator, Optional, Sequence, Set, cast


def in_cache(cache: Dict[str, Any], keys: Sequence[str]) -> Optional[Set[Sequence[str]]]:
if keys[0] in cache:
if len(keys) > 1:
return in_cache(cache[keys[-0]], keys[1:])
return cast(Set[Sequence[str]], cache[keys[0]])
else:
return None


class FastPolicy(Container[Sequence[str]]):
_cache: Dict[str, Any]
_current_filter: Optional[Set[Sequence[str]]]
_cache_key_order: Sequence[int]

def __init__(self, cache_key_order: Sequence[int]) -> None:
self._cache = {}
self._current_filter = None
self._cache_key_order = cache_key_order

def __iter__(self) -> Iterator[Sequence[str]]:
yield from self.__get_policy()

def __len__(self) -> int:
return len(list(self.__get_policy()))

def __contains__(self, item: object) -> bool:
if not isinstance(item, (list, tuple)) or len(self._cache_key_order) >= len(item):
return False
keys = [item[x] for x in self._cache_key_order]
exists = in_cache(self._cache, keys)
if not exists:
return False
return tuple(item) in exists

def __getitem__(self, item: int) -> Sequence[str]:
for i, entry in enumerate(self):
if i == item:
return entry
raise KeyError("No such value exists")

def append(self, item: Sequence[str]) -> None:
cache = self._cache
keys = [item[x] for x in self._cache_key_order]

for key in keys[:-1]:
if key not in cache:
cache[key] = dict()
cache = cache[key]
if keys[-1] not in cache:
cache[keys[-1]] = set()

cache[keys[-1]].add(tuple(item))

def remove(self, policy: Sequence[str]) -> bool:
keys = [policy[x] for x in self._cache_key_order]
exists = in_cache(self._cache, keys)
if not exists:
return True

exists.remove(tuple(policy))
return True

def __get_policy(self) -> Iterable[Sequence[str]]:
if self._current_filter is not None:
return (list(x) for x in self._current_filter)
else:
return (list(v2) for v in self._cache.values() for v1 in v.values() for v2 in v1)

def apply_filter(self, *keys: str) -> None:
value = in_cache(self._cache, keys)
self._current_filter = value or set()

def clear_filter(self) -> None:
self._current_filter = None


@contextmanager
def fast_policy_filter(policy: FastPolicy, *keys: str) -> Iterator[None]:
try:
policy.apply_filter(*keys)
yield
finally:
policy.clear_filter()
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from .test_frontend import TestFrontend
from .test_management_api import TestManagementApi, TestManagementApiSynced
from .test_rbac_api import TestRbacApi, TestRbacApiSynced
from .test_enforcer_fast import TestFastEnforcer
from . import benchmarks
from . import config
from . import model
Expand Down
1 change: 1 addition & 0 deletions tests/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
# limitations under the License.

from .test_policy import TestPolicy
from .test_policy_fast import TestContextManager, TestFastPolicy
Loading

0 comments on commit a7ed0bb

Please sign in to comment.