AccessGate 0.2.0a1__py3-none-any.whl
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.
- access_gate/__init__.py +118 -0
- access_gate/abac.py +1081 -0
- access_gate/adapters/__init__.py +5 -0
- access_gate/adapters/django.py +177 -0
- access_gate/engine.py +224 -0
- access_gate/exceptions.py +13 -0
- access_gate/integrity.py +262 -0
- access_gate/mcp/__init__.py +1 -0
- access_gate/mcp/agent-contract.json +419 -0
- access_gate/mcp/server.py +378 -0
- access_gate/policies.py +242 -0
- access_gate/protocols.py +51 -0
- access_gate/py.typed +0 -0
- access_gate/schema/abac-policy-v1.json +113 -0
- access_gate/strategies.py +28 -0
- access_gate/types.py +315 -0
- accessgate-0.2.0a1.dist-info/METADATA +287 -0
- accessgate-0.2.0a1.dist-info/RECORD +22 -0
- accessgate-0.2.0a1.dist-info/WHEEL +5 -0
- accessgate-0.2.0a1.dist-info/entry_points.txt +2 -0
- accessgate-0.2.0a1.dist-info/licenses/LICENSE +21 -0
- accessgate-0.2.0a1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: AccessGate
|
|
3
|
+
Version: 0.2.0a1
|
|
4
|
+
Summary: Composable, default-deny authorization for Python applications.
|
|
5
|
+
Author-email: Tunet Ltd <alex@tunet.xyz>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Tunet-xyz/access_gate
|
|
8
|
+
Project-URL: Documentation, https://github.com/Tunet-xyz/access_gate#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/Tunet-xyz/access_gate
|
|
10
|
+
Project-URL: Issues, https://github.com/Tunet-xyz/access_gate/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/Tunet-xyz/access_gate/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: authorization,access-control,rbac,abac,security,policy
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Security
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Provides-Extra: django
|
|
26
|
+
Requires-Dist: Django>=4.2; extra == "django"
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: Django>=4.2; extra == "dev"
|
|
29
|
+
Requires-Dist: bandit>=1.7; extra == "dev"
|
|
30
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
31
|
+
Requires-Dist: django-stubs>=4.2; extra == "dev"
|
|
32
|
+
Requires-Dist: hypothesis<7,>=6.112; extra == "dev"
|
|
33
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
34
|
+
Requires-Dist: pip-audit>=2.7; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
37
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
38
|
+
Requires-Dist: setuptools>=68; extra == "dev"
|
|
39
|
+
Requires-Dist: tomli>=2; python_version < "3.11" and extra == "dev"
|
|
40
|
+
Requires-Dist: wheel; extra == "dev"
|
|
41
|
+
Provides-Extra: assurance
|
|
42
|
+
Requires-Dist: hypothesis<7,>=6.112; extra == "assurance"
|
|
43
|
+
Requires-Dist: mutmut==3.7.0; extra == "assurance"
|
|
44
|
+
Requires-Dist: pytest>=8.0; extra == "assurance"
|
|
45
|
+
Provides-Extra: fuzz
|
|
46
|
+
Requires-Dist: atheris==3.0.0; extra == "fuzz"
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
|
|
49
|
+
# AccessGate
|
|
50
|
+
|
|
51
|
+
Composable, default-deny RBAC, ABAC, and relationship-aware authorization for
|
|
52
|
+
Python applications.
|
|
53
|
+
|
|
54
|
+
AccessGate answers one question: **may this subject perform this action on
|
|
55
|
+
this resource in this context?** It provides a small framework-neutral kernel,
|
|
56
|
+
secure decision defaults, and extension interfaces for application-specific
|
|
57
|
+
identity, policy, audit, and framework integration.
|
|
58
|
+
|
|
59
|
+
It is complementary to authentication and session-security packages such as
|
|
60
|
+
SessionArmor. Those establish who the caller is and whether the session can be
|
|
61
|
+
trusted; AccessGate decides what that caller is allowed to do.
|
|
62
|
+
|
|
63
|
+
> **Status:** early alpha. Security controls and assurance evidence are being
|
|
64
|
+
> developed rigorously, but the public API may evolve before 1.0. This package
|
|
65
|
+
> has not received an independent audit or any government/NATO accreditation.
|
|
66
|
+
|
|
67
|
+
## Design guarantees
|
|
68
|
+
|
|
69
|
+
- **Default deny:** abstention never grants access.
|
|
70
|
+
- **Deny overrides:** any explicit denial wins over grants by default.
|
|
71
|
+
- **Fail closed:** policy and audit failures deny access by default.
|
|
72
|
+
- **Exact matching:** built-in role, action, owner, and tenant comparisons never
|
|
73
|
+
use substring matching.
|
|
74
|
+
- **Safe ABAC:** missing attributes and operator failures are indeterminate,
|
|
75
|
+
cannot be negated into access, and deny by default.
|
|
76
|
+
- **Immutable inputs:** request attributes, literal values, and decision metadata
|
|
77
|
+
are recursively snapshotted before evaluation or audit.
|
|
78
|
+
- **Bounded evaluation:** serialized policies, condition trees, MCP messages, and
|
|
79
|
+
collection workloads have explicit denial-of-service limits.
|
|
80
|
+
- **Verifiable policy identity:** canonical fingerprints, ordered policy-set
|
|
81
|
+
fingerprints, externally verified signed envelopes, and engine-owned policy
|
|
82
|
+
deployment identifiers support change control without embedding key custody.
|
|
83
|
+
- **No expression evaluation:** declarative policies use registered operators
|
|
84
|
+
and mapping-only attribute traversal—never Python `eval` or object traversal.
|
|
85
|
+
- **Framework-neutral core:** no runtime dependencies and no ORM, token, or user
|
|
86
|
+
model assumptions.
|
|
87
|
+
- **Custom from day one:** policies, decision strategies, identity/resource
|
|
88
|
+
resolvers, audit sinks, and framework adapters are public extension points.
|
|
89
|
+
|
|
90
|
+
`Subject`, `Resource`, `AuthorizationRequest`, and `Decision` expose `to_dict()`
|
|
91
|
+
for JSON-friendly snapshots of their otherwise immutable data.
|
|
92
|
+
|
|
93
|
+
## Installation
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pip install AccessGate
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
For the optional Django adapter:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pip install "AccessGate[django]"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Quick start
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from access_gate import (
|
|
109
|
+
AuthorizationEngine,
|
|
110
|
+
Resource,
|
|
111
|
+
RolePolicy,
|
|
112
|
+
Subject,
|
|
113
|
+
TenantBoundaryPolicy,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
engine = AuthorizationEngine(
|
|
117
|
+
[
|
|
118
|
+
TenantBoundaryPolicy(actions={"incident.read"}),
|
|
119
|
+
RolePolicy({"incident_manager"}, actions={"incident.read"}),
|
|
120
|
+
]
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
decision = engine.authorize(
|
|
124
|
+
Subject(
|
|
125
|
+
"user-42",
|
|
126
|
+
roles=frozenset({"incident_manager"}),
|
|
127
|
+
attributes={"tenant_id": "acme"},
|
|
128
|
+
),
|
|
129
|
+
"incident.read",
|
|
130
|
+
Resource("incident", "INC-123", {"tenant_id": "acme"}),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
assert decision.allowed
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Tenant boundaries deliberately do not grant access. They abstain when the
|
|
137
|
+
tenant matches and deny when it is missing or different; another policy must
|
|
138
|
+
positively allow the action.
|
|
139
|
+
|
|
140
|
+
## Attribute-based access control
|
|
141
|
+
|
|
142
|
+
ABAC policies can compare subject, resource, action, and context attributes:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from access_gate import (
|
|
146
|
+
ActionAttribute,
|
|
147
|
+
AllOf,
|
|
148
|
+
AttributePolicy,
|
|
149
|
+
ContextAttribute,
|
|
150
|
+
ResourceAttribute,
|
|
151
|
+
SubjectAttribute,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
document_access = AttributePolicy(
|
|
155
|
+
AllOf(
|
|
156
|
+
SubjectAttribute("department").equals(ResourceAttribute("department")),
|
|
157
|
+
SubjectAttribute("clearance").greater_than_or_equal(
|
|
158
|
+
ResourceAttribute("classification")
|
|
159
|
+
),
|
|
160
|
+
ActionAttribute("risk").less_than_or_equal(3),
|
|
161
|
+
ContextAttribute("device.trusted").equals(True),
|
|
162
|
+
),
|
|
163
|
+
actions={"document.read"},
|
|
164
|
+
name="document_access",
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Conditions support `AllOf`, `AnyOf`, and `Not`, multi-valued attributes, exact
|
|
169
|
+
membership and set relations, numeric comparisons, and explicit existence
|
|
170
|
+
checks. `Not` preserves indeterminate outcomes, so a missing attribute never
|
|
171
|
+
becomes a grant merely because a condition was negated.
|
|
172
|
+
|
|
173
|
+
Policies serialize to the versioned `access_gate.policy.v1` format:
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from access_gate import dumps_policy, loads_policy
|
|
177
|
+
|
|
178
|
+
document = dumps_policy(document_access)
|
|
179
|
+
restored = loads_policy(document)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
For controlled deployments, identify the exact ordered policy set and pass that
|
|
183
|
+
identifier into the engine so every final and audited decision is attributable:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from access_gate import AuthorizationEngine, policy_set_fingerprint
|
|
187
|
+
|
|
188
|
+
policy_set_id = policy_set_fingerprint([document_access])
|
|
189
|
+
engine = AuthorizationEngine([document_access], policy_set_id=policy_set_id)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`dumps_signed_policy()` and `loads_signed_policy()` accept application-supplied
|
|
193
|
+
signer/verifier protocols. AccessGate deliberately does not choose algorithms,
|
|
194
|
+
store keys, or implement a trust store. See
|
|
195
|
+
[`docs/POLICY_FORMAT.md`](docs/POLICY_FORMAT.md).
|
|
196
|
+
|
|
197
|
+
See [`docs/abac.md`](docs/abac.md) for the full data model, operator semantics,
|
|
198
|
+
customization rules, JSON format, and rollout guidance.
|
|
199
|
+
|
|
200
|
+
## Application-defined policies
|
|
201
|
+
|
|
202
|
+
Implement the `Policy` protocol directly or wrap a function:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
from access_gate import Decision, FunctionPolicy
|
|
206
|
+
|
|
207
|
+
def published_incidents(request):
|
|
208
|
+
resource = request.resource
|
|
209
|
+
if resource and resource.attributes.get("published") is True:
|
|
210
|
+
return Decision.allow("The incident is public.", policy="published")
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
policy = FunctionPolicy("published", published_incidents)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
A policy returns an allow/deny `Decision`, or `None` to abstain. Custom decision
|
|
217
|
+
strategies can replace deny-overrides where a domain needs different semantics.
|
|
218
|
+
Policies exposing a validated `frozenset` in an `actions` attribute are indexed
|
|
219
|
+
by the engine and skipped for unrelated actions. `FunctionPolicy` supports this
|
|
220
|
+
directly through its `actions` argument. Use `decide_many()` for ordered batch
|
|
221
|
+
evaluation; every request still receives its own decision and audit event.
|
|
222
|
+
|
|
223
|
+
For mixed RBAC/ABAC grants, configure alternative grant policies to abstain on
|
|
224
|
+
a miss. Explicit boundary denials still override every grant.
|
|
225
|
+
|
|
226
|
+
## Django adapter
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
from access_gate import AuthorizationEngine, RolePolicy
|
|
230
|
+
from access_gate.adapters.django import DjangoAuthorizer
|
|
231
|
+
|
|
232
|
+
authorizer = DjangoAuthorizer(
|
|
233
|
+
AuthorizationEngine([RolePolicy({"admin"}, actions={"users.list"})])
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
@authorizer.require("users.list")
|
|
237
|
+
def user_list(request):
|
|
238
|
+
...
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The default Django subject resolver only reads `pk`, `is_authenticated`,
|
|
242
|
+
`access_gate_roles`, and `access_gate_attributes`. Real applications should
|
|
243
|
+
normally provide a resolver that validates and maps their exact identity claims.
|
|
244
|
+
The adapter also accepts custom context and action-attribute resolvers for ABAC.
|
|
245
|
+
Synchronous and asynchronous Django views are supported. Async views may use
|
|
246
|
+
awaitable subject, resource, context, action-attribute, and denial resolvers.
|
|
247
|
+
|
|
248
|
+
## MCP server
|
|
249
|
+
|
|
250
|
+
AccessGate includes a dependency-free stdio MCP server:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
accessgate-mcp
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
From a source checkout, run `python mcp/server.py`. The server exposes package
|
|
257
|
+
capabilities, authorization-model guidance, the ABAC JSON Schema, exact built-in
|
|
258
|
+
operator discovery, and read-only policy validation. It does not authenticate
|
|
259
|
+
users, evaluate production identity data, or change application policy.
|
|
260
|
+
|
|
261
|
+
## Project boundary
|
|
262
|
+
|
|
263
|
+
AccessGate does not authenticate users, validate JWTs, secure sessions, query
|
|
264
|
+
application permissions, or decide how tenants and resources are represented.
|
|
265
|
+
Those are application and adapter responsibilities. See
|
|
266
|
+
[`docs/architecture.md`](docs/architecture.md) and
|
|
267
|
+
[`docs/customization.md`](docs/customization.md). AccessGate provides its own
|
|
268
|
+
policy format but does not claim wire compatibility with XACML, Cedar, Rego, or
|
|
269
|
+
other policy languages.
|
|
270
|
+
|
|
271
|
+
## Development
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
python -m pip install -e ".[dev]"
|
|
275
|
+
ruff check src tests
|
|
276
|
+
mypy src/access_gate
|
|
277
|
+
pytest --cov=access_gate --cov-report=term-missing
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Security reviewers should begin with the
|
|
281
|
+
[`threat model`](docs/THREAT_MODEL.md),
|
|
282
|
+
[`assurance case`](docs/SECURITY_ASSURANCE.md), and
|
|
283
|
+
[`audit scope`](docs/AUDIT_SCOPE.md).
|
|
284
|
+
|
|
285
|
+
## License
|
|
286
|
+
|
|
287
|
+
MIT (c) Tunet Ltd.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
access_gate/__init__.py,sha256=LVnwcFWROcXq4AX5Z46ZCiv552Xu1UlH-rhKak-njJc,2621
|
|
2
|
+
access_gate/abac.py,sha256=uuSYLxXRMPOIcnHuWjCIKOzAlk-Wbon3B6Zpgb1inHo,40453
|
|
3
|
+
access_gate/engine.py,sha256=WRm6hreSK5L7HOcyce6KnfDrkx6MOcX-P_dTmCh4jJI,9183
|
|
4
|
+
access_gate/exceptions.py,sha256=TrdBCvgmI1HEORAmfbIk9DfwoL-FtcdyW8BojDitT5Q,330
|
|
5
|
+
access_gate/integrity.py,sha256=DYZkD3BSon7KpAiHRYne-glwufOuBNckJ5L4JI5MD-I,9819
|
|
6
|
+
access_gate/policies.py,sha256=Kz-n6Ldkuas7pXGT3br-VUUv1cXWgeq78g0D9qusfrY,8949
|
|
7
|
+
access_gate/protocols.py,sha256=NXvZrU02hUvHk3RiMC5n2DKnWvPoZ7BMo451A0c4AJo,1489
|
|
8
|
+
access_gate/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
access_gate/strategies.py,sha256=UFkcQvBQ1XYXOxVx0aUEWG0bGZD6bquIc8TSLfMGq4c,792
|
|
10
|
+
access_gate/types.py,sha256=3hg0ZXzQLgI2YCKcmFYcAl69MS5ALZnwjR2NG3jM5nw,10838
|
|
11
|
+
access_gate/adapters/__init__.py,sha256=MXRN1tPbn2gh8GQOv2TTfH7j55u2MrYFl_wWql7bEC8,140
|
|
12
|
+
access_gate/adapters/django.py,sha256=7OL8qlb1KOwD8sSL0E42RO-fyumNON8iJuoPFcu-Ryc,6738
|
|
13
|
+
access_gate/mcp/__init__.py,sha256=ynS4M9291AsqtMsNWQVzUYHBz-IRBg4Z4G_sRFtqtGo,54
|
|
14
|
+
access_gate/mcp/agent-contract.json,sha256=zh7d_tCZVmsCFVkGFCq8D0MkK49aaOxCdFl_g1BFVK8,13042
|
|
15
|
+
access_gate/mcp/server.py,sha256=hT1CmO5KFy8QTMjvx3Pem1tWbNietzC9FpbY1IBxkBA,14055
|
|
16
|
+
access_gate/schema/abac-policy-v1.json,sha256=JhtWl1fB_HhrsLriX4yUsYq6LXM0zn_O6edDsUmYNlw,3071
|
|
17
|
+
accessgate-0.2.0a1.dist-info/licenses/LICENSE,sha256=yCFMhogmtelZAEkUxqTeRzkI8zJgDd3_zUcsxnvXZUw,1066
|
|
18
|
+
accessgate-0.2.0a1.dist-info/METADATA,sha256=KPC39HEnx46HYc7BIgLi0wzhyNLcPIic3LSEqPnT8Mg,10462
|
|
19
|
+
accessgate-0.2.0a1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
20
|
+
accessgate-0.2.0a1.dist-info/entry_points.txt,sha256=Fhjz8mBsQwMTykvGu8rAfn9ywOWd2KGz6D3Z6PbG7Iw,63
|
|
21
|
+
accessgate-0.2.0a1.dist-info/top_level.txt,sha256=YSWY3-opvADQ8U0_tQ4bB6juBasJhkr4E3dQjJT0Iug,12
|
|
22
|
+
accessgate-0.2.0a1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tunet Ltd
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
access_gate
|