|
| 1 | +import re |
1 | 2 | from typing import Any, Literal |
2 | 3 |
|
3 | 4 | from entitled.exceptions import AuthorizationException |
|
8 | 9 |
|
9 | 10 | class Client: |
10 | 11 | def __init__(self): |
11 | | - self._policy_registry: dict[type, type[Policy[Any]]] = {} |
| 12 | + self._policy_registry: dict[type, Policy[Any]] = {} |
12 | 13 | self._rule_registry: dict[str, Rule[Any]] = {} |
13 | 14 |
|
14 | 15 | def define_rule(self, name: str, callable: RuleProto[Actor]) -> Rule[Actor]: |
15 | 16 | rule = Rule(name, callable) |
16 | 17 | self._rule_registry[rule.name] = rule |
17 | 18 | return rule |
18 | 19 |
|
19 | | - def _register_policy(self, policy: type[Policy[Any]]): |
20 | | - resource_type = getattr(policy, "__orig_bases__")[0].__args__[0] |
| 20 | + def register_policy(self, policy: Policy[Any]): |
| 21 | + resource_type = getattr(policy, "__orig_class__").__args__[0] |
21 | 22 | self._policy_registry[resource_type] = policy |
22 | 23 |
|
23 | | - def _resolve_rule(self, name: str, resource: Any) -> Rule[Any] | None: |
| 24 | + def _resolve_policy(self, resource: Any) -> Policy[Any] | None: |
24 | 25 | lookup_key = resource if isinstance(resource, type) else type(resource) |
25 | 26 | policy = self._policy_registry.get(lookup_key, None) |
26 | | - if policy is not None: |
27 | | - rule = getattr(policy, name, None) |
28 | | - if rule is not None and hasattr(rule, "_is_rule"): |
29 | | - return Rule(name, rule) |
30 | | - return self._rule_registry.get(name, None) |
| 27 | + return policy |
31 | 28 |
|
32 | 29 | async def inspect( |
33 | 30 | self, |
34 | 31 | name: str, |
35 | | - actor: Actor, |
| 32 | + actor: Any, |
36 | 33 | resource: Any, |
37 | 34 | *args: Any, |
38 | 35 | **kwargs: Any, |
39 | 36 | ) -> Response: |
40 | | - rule = self._resolve_rule(name, resource) |
41 | | - if rule is not None: |
42 | | - return await rule.inspect(actor, resource, *args, **kwargs) |
43 | | - return Err(f"No rule found with name '{name}'") |
| 37 | + policy = self._resolve_policy(resource) |
| 38 | + if policy is not None: |
| 39 | + return await policy.inspect(name, actor, resource, *args, **kwargs) |
| 40 | + return Err(f"No policy found with name '{name}'") |
44 | 41 |
|
45 | 42 | async def allows( |
46 | 43 | self, |
47 | 44 | name: str, |
48 | | - actor: Actor, |
| 45 | + actor: Any, |
| 46 | + resource: Any, |
49 | 47 | *args: Any, |
50 | 48 | **kwargs: Any, |
51 | 49 | ) -> bool: |
52 | | - if name in self._rule_registry: |
53 | | - return await self._rule_registry[name].allows(actor, *args, **kwargs) |
54 | | - return False |
| 50 | + return (await self.inspect(name, actor, resource, *args, **kwargs)).allowed() |
55 | 51 |
|
56 | 52 | async def denies( |
57 | 53 | self, |
58 | 54 | name: str, |
59 | | - actor: Actor, |
| 55 | + actor: Any, |
| 56 | + resource: Any, |
60 | 57 | *args: Any, |
61 | 58 | **kwargs: Any, |
62 | 59 | ) -> bool: |
63 | | - return not self.allows(name, actor, *args, **kwargs) |
| 60 | + return not await self.allows(name, actor, resource, *args, **kwargs) |
64 | 61 |
|
65 | 62 | async def authorize( |
66 | 63 | self, |
67 | 64 | name: str, |
68 | | - actor: Actor, |
| 65 | + actor: Any, |
| 66 | + resource: Any, |
69 | 67 | *args: Any, |
70 | 68 | **kwargs: Any, |
71 | 69 | ) -> Literal[True]: |
72 | | - if name in self._rule_registry: |
73 | | - return await self._rule_registry[name].authorize(actor, *args, **kwargs) |
74 | | - raise AuthorizationException(f"No rule found with name '{name}'") |
| 70 | + result = await self.inspect(name, actor, resource, *args, **kwargs) |
| 71 | + if not result.allowed(): |
| 72 | + raise AuthorizationException(result.message()) |
| 73 | + return True |
| 74 | + |
| 75 | + async def grants( |
| 76 | + self, |
| 77 | + actor: Any, |
| 78 | + resource: Any, |
| 79 | + *args: Any, |
| 80 | + **kwargs: Any, |
| 81 | + ) -> dict[str, bool]: |
| 82 | + policy = self._resolve_policy(resource) |
| 83 | + if policy is None: |
| 84 | + return {} |
| 85 | + return await policy.grants(actor, resource, *args, **kwargs) |
0 commit comments