policy-pattern 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/PKG-INFO +1 -1
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/evaluator.py +37 -9
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/pyproject.toml +1 -1
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/pyproject.toml.orig +1 -1
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/LICENSE.md +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/README.md +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/__init__.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/adapters/__init__.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/adapters/_db_protocols.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/adapters/_tuple_mapping.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/adapters/memory.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/adapters/postgres.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/errors/__init__.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/errors/base_error.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/errors/evaluation_error.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/errors/identifier_error.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/errors/index_entry_too_large_error.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/errors/model_validation_error.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/errors/mutation_error.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/errors/relation_error.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/errors/unknown_name_error.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/errors/unknown_type_error.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/__init__.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/_schema_validation.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/compiled_model.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/direct_subject.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/identifier.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/model.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/mutation.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/object_reference.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/operators.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/reference.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/relation.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/subject.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/subject_set.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/tuple.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/tuple_to_userset.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/models/type.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/ports/__init__.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/ports/tuple_store.py +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/py.typed +0 -0
- {policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/sql/postgres/0001_create_policy_tuples.sql +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: policy-pattern
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A minimal, embeddable ReBAC authorization engine for Python, inspired by Google Zanzibar.
|
|
5
5
|
Keywords: python,authorization,access-control,rebac,zanzibar,security
|
|
6
6
|
Author: Guillem Pozo Sebastián
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Evaluator: resolves check() over a CompiledModel and a TupleStore.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from collections.abc import Sequence
|
|
5
6
|
from dataclasses import dataclass, field
|
|
6
7
|
|
|
7
8
|
from .errors import EvaluationError, UnknownNameError, UnknownTypeError
|
|
@@ -24,7 +25,11 @@ from .ports import TupleStore, View
|
|
|
24
25
|
@dataclass(frozen=True, slots=True)
|
|
25
26
|
class EvaluationBudget:
|
|
26
27
|
"""
|
|
27
|
-
|
|
28
|
+
Resource limits for one `check()` or `check_many()` call.
|
|
29
|
+
|
|
30
|
+
`max_nodes` and `max_store_reads` accumulate across a `check_many()` call.
|
|
31
|
+
`max_depth` bounds the live `context.visited` frontier and resets as each
|
|
32
|
+
request finishes, so it does not accumulate across the batch.
|
|
28
33
|
|
|
29
34
|
No `__post_init__` validation — a degenerate value (e.g. `max_depth=0`)
|
|
30
35
|
is well-defined without a guard: it simply fails on the first node
|
|
@@ -35,16 +40,16 @@ class EvaluationBudget:
|
|
|
35
40
|
EvaluationBudget(max_depth=10, max_nodes=500, max_store_reads=100)
|
|
36
41
|
"""
|
|
37
42
|
|
|
38
|
-
# Maximum recursion depth for
|
|
43
|
+
# Maximum recursion depth for one call. Each recursive call
|
|
39
44
|
# to `_check_name()` adds one to the depth; exceeding this limit raises an
|
|
40
45
|
# `EvaluationError`.
|
|
41
46
|
max_depth: int = 25
|
|
42
47
|
|
|
43
|
-
# Maximum number of nodes visited during
|
|
48
|
+
# Maximum number of nodes visited during one call. Each
|
|
44
49
|
# call to `_check_name()` increments this count; exceeding this limit raises an `EvaluationError`.
|
|
45
50
|
max_nodes: int = 10_000
|
|
46
51
|
|
|
47
|
-
# Maximum number of store reads during
|
|
52
|
+
# Maximum number of store reads during one call. Each call
|
|
48
53
|
# to `_charge_read()` increments this count; exceeding this limit raises an `EvaluationError`.
|
|
49
54
|
max_store_reads: int = 1_000
|
|
50
55
|
|
|
@@ -52,7 +57,7 @@ class EvaluationBudget:
|
|
|
52
57
|
@dataclass(slots=True)
|
|
53
58
|
class EvaluationContext:
|
|
54
59
|
"""
|
|
55
|
-
Live bookkeeping for exactly one `check()` call.
|
|
60
|
+
Live bookkeeping for exactly one `check()` or `check_many()` call.
|
|
56
61
|
|
|
57
62
|
Deliberately mutable, unlike almost everything else in this package —
|
|
58
63
|
this is not a domain value, it is the in-flight state of one
|
|
@@ -63,13 +68,19 @@ class EvaluationContext:
|
|
|
63
68
|
(added before recursing, discarded after returning) — anything
|
|
64
69
|
permanently resolved lives in `memo` instead, so the two never overlap
|
|
65
70
|
in meaning.
|
|
71
|
+
|
|
72
|
+
The key carries the subject because one `check()` has a constant subject,
|
|
73
|
+
while `check_many()` can mix subjects; a narrower key could reuse one
|
|
74
|
+
subject's cached decision for another and cause a false allow.
|
|
66
75
|
"""
|
|
67
76
|
|
|
68
77
|
view: View
|
|
69
78
|
budget: EvaluationBudget
|
|
70
|
-
visited: set[tuple[ObjectReference, str]] = field(
|
|
71
|
-
|
|
72
|
-
|
|
79
|
+
visited: set[tuple[Subject, ObjectReference, str]] = field(
|
|
80
|
+
default_factory=lambda: set[tuple[Subject, ObjectReference, str]]()
|
|
81
|
+
)
|
|
82
|
+
memo: dict[tuple[Subject, ObjectReference, str], bool] = field(
|
|
83
|
+
default_factory=lambda: dict[tuple[Subject, ObjectReference, str], bool]()
|
|
73
84
|
)
|
|
74
85
|
node_count: int = 0
|
|
75
86
|
store_read_count: int = 0
|
|
@@ -120,11 +131,28 @@ class Evaluator:
|
|
|
120
131
|
context = EvaluationContext(view=view, budget=budget)
|
|
121
132
|
return self._check_name(subject, name, object, context)
|
|
122
133
|
|
|
134
|
+
def check_many(
|
|
135
|
+
self,
|
|
136
|
+
requests: Sequence[tuple[Subject, str, ObjectReference]],
|
|
137
|
+
budget: EvaluationBudget | None = None,
|
|
138
|
+
) -> list[bool]:
|
|
139
|
+
"""
|
|
140
|
+
Returns one decision per request, preserving input order and duplicates.
|
|
141
|
+
|
|
142
|
+
The whole batch shares one snapshot, context, and budget. An error on
|
|
143
|
+
any request aborts the call and propagates without partial results.
|
|
144
|
+
"""
|
|
145
|
+
if budget is None:
|
|
146
|
+
budget = EvaluationBudget()
|
|
147
|
+
with self._store.snapshot() as view:
|
|
148
|
+
context = EvaluationContext(view=view, budget=budget)
|
|
149
|
+
return [self._check_name(subject, name, object, context) for subject, name, object in requests]
|
|
150
|
+
|
|
123
151
|
def _check_name(self, subject: Subject, name: str, object: ObjectReference, context: EvaluationContext) -> bool:
|
|
124
152
|
"""
|
|
125
153
|
Dispatches (object, name) to a relation or permission check, with cycle/memo/budget bookkeeping.
|
|
126
154
|
"""
|
|
127
|
-
key = (object, name)
|
|
155
|
+
key = (subject, object, name)
|
|
128
156
|
if key in context.memo:
|
|
129
157
|
return context.memo[key]
|
|
130
158
|
if key in context.visited:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/errors/index_entry_too_large_error.py
RENAMED
|
File without changes
|
{policy_pattern-0.3.0 → policy_pattern-0.4.0}/policy_pattern/errors/model_validation_error.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|