fastpermit 0.1.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.
Files changed (37) hide show
  1. fastpermit-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +41 -0
  2. fastpermit-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +22 -0
  3. fastpermit-0.1.0/.github/workflows/ci.yml +51 -0
  4. fastpermit-0.1.0/.github/workflows/release.yml +78 -0
  5. fastpermit-0.1.0/CHANGELOG.md +42 -0
  6. fastpermit-0.1.0/CONTRIBUTING.md +33 -0
  7. fastpermit-0.1.0/LICENSE +21 -0
  8. fastpermit-0.1.0/MANIFEST.in +6 -0
  9. fastpermit-0.1.0/Makefile +29 -0
  10. fastpermit-0.1.0/PKG-INFO +383 -0
  11. fastpermit-0.1.0/README.md +346 -0
  12. fastpermit-0.1.0/examples/basic.py +100 -0
  13. fastpermit-0.1.0/pyproject.toml +97 -0
  14. fastpermit-0.1.0/setup.cfg +4 -0
  15. fastpermit-0.1.0/src/fastpermit/__init__.py +37 -0
  16. fastpermit-0.1.0/src/fastpermit/backends/__init__.py +4 -0
  17. fastpermit-0.1.0/src/fastpermit/backends/base.py +17 -0
  18. fastpermit-0.1.0/src/fastpermit/backends/memory.py +42 -0
  19. fastpermit-0.1.0/src/fastpermit/core/__init__.py +12 -0
  20. fastpermit-0.1.0/src/fastpermit/core/context.py +35 -0
  21. fastpermit-0.1.0/src/fastpermit/core/evaluator.py +84 -0
  22. fastpermit-0.1.0/src/fastpermit/core/helpers.py +24 -0
  23. fastpermit-0.1.0/src/fastpermit/core/permission.py +164 -0
  24. fastpermit-0.1.0/src/fastpermit/integrations/__init__.py +141 -0
  25. fastpermit-0.1.0/src/fastpermit/permissions.py +108 -0
  26. fastpermit-0.1.0/src/fastpermit/principal.py +38 -0
  27. fastpermit-0.1.0/src/fastpermit/py.typed +0 -0
  28. fastpermit-0.1.0/src/fastpermit.egg-info/PKG-INFO +383 -0
  29. fastpermit-0.1.0/src/fastpermit.egg-info/SOURCES.txt +35 -0
  30. fastpermit-0.1.0/src/fastpermit.egg-info/dependency_links.txt +1 -0
  31. fastpermit-0.1.0/src/fastpermit.egg-info/requires.txt +11 -0
  32. fastpermit-0.1.0/src/fastpermit.egg-info/top_level.txt +1 -0
  33. fastpermit-0.1.0/tests/test_context_and_backend.py +53 -0
  34. fastpermit-0.1.0/tests/test_evaluator.py +39 -0
  35. fastpermit-0.1.0/tests/test_integration.py +174 -0
  36. fastpermit-0.1.0/tests/test_object_permissions.py +107 -0
  37. fastpermit-0.1.0/tests/test_permissions.py +154 -0
@@ -0,0 +1,41 @@
1
+ name: Bug report
2
+ description: Report incorrect or unexpected FastPermit behavior
3
+ title: "[Bug]: "
4
+ labels: ["bug"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: Thanks for helping improve FastPermit. For security vulnerabilities, follow SECURITY.md instead of filing a public issue.
9
+ - type: input
10
+ id: version
11
+ attributes:
12
+ label: FastPermit version
13
+ placeholder: 0.1.0
14
+ validations:
15
+ required: true
16
+ - type: input
17
+ id: python
18
+ attributes:
19
+ label: Python version
20
+ placeholder: 3.13
21
+ validations:
22
+ required: true
23
+ - type: textarea
24
+ id: reproduction
25
+ attributes:
26
+ label: Minimal reproduction
27
+ description: Include the smallest code sample that reproduces the problem.
28
+ validations:
29
+ required: true
30
+ - type: textarea
31
+ id: expected
32
+ attributes:
33
+ label: Expected behavior
34
+ validations:
35
+ required: true
36
+ - type: textarea
37
+ id: actual
38
+ attributes:
39
+ label: Actual behavior
40
+ validations:
41
+ required: true
@@ -0,0 +1,22 @@
1
+ name: Feature request
2
+ description: Propose a focused improvement to FastPermit
3
+ title: "[Feature]: "
4
+ labels: ["enhancement"]
5
+ body:
6
+ - type: textarea
7
+ id: problem
8
+ attributes:
9
+ label: Problem
10
+ description: What authorization problem would this solve?
11
+ validations:
12
+ required: true
13
+ - type: textarea
14
+ id: proposal
15
+ attributes:
16
+ label: Proposed API or behavior
17
+ validations:
18
+ required: true
19
+ - type: textarea
20
+ id: alternatives
21
+ attributes:
22
+ label: Alternatives considered
@@ -0,0 +1,51 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+ cache: pip
23
+
24
+ - name: Install package
25
+ run: python -m pip install -e '.[dev]'
26
+
27
+ - name: Mypy
28
+ run: mypy src/fastpermit
29
+
30
+ - name: Pytest
31
+ run: pytest --cov=fastpermit --cov-report=term-missing
32
+
33
+ package:
34
+ runs-on: ubuntu-latest
35
+
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+
39
+ - uses: actions/setup-python@v5
40
+ with:
41
+ python-version: "3.13"
42
+ cache: pip
43
+
44
+ - name: Install build tools
45
+ run: python -m pip install build twine
46
+
47
+ - name: Build distributions
48
+ run: python -m build
49
+
50
+ - name: Validate distributions
51
+ run: python -m twine check dist/*
@@ -0,0 +1,78 @@
1
+ name: Release
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ build:
9
+ name: Build release distributions
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: read
13
+
14
+ steps:
15
+ - name: Checkout
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.13"
22
+ cache: pip
23
+
24
+ - name: Verify release tag
25
+ shell: bash
26
+ run: |
27
+ python - <<'PY'
28
+ import os
29
+ import tomllib
30
+ from pathlib import Path
31
+
32
+ project = tomllib.loads(Path("pyproject.toml").read_text())["project"]
33
+ expected = f"v{project['version']}"
34
+ actual = os.environ["GITHUB_REF_NAME"]
35
+
36
+ if actual != expected:
37
+ raise SystemExit(
38
+ f"Release tag {actual!r} does not match package version {expected!r}."
39
+ )
40
+ PY
41
+
42
+ - name: Install build tools
43
+ run: python -m pip install --upgrade build twine
44
+
45
+ - name: Build distributions
46
+ run: python -m build
47
+
48
+ - name: Validate distributions
49
+ run: python -m twine check dist/*
50
+
51
+ - name: Upload release distributions
52
+ uses: actions/upload-artifact@v4
53
+ with:
54
+ name: python-package-distributions
55
+ path: dist/
56
+ if-no-files-found: error
57
+
58
+ publish:
59
+ name: Publish to PyPI
60
+ needs: build
61
+ runs-on: ubuntu-latest
62
+ environment:
63
+ name: pypi
64
+ url: https://pypi.org/p/fastpermit
65
+ permissions:
66
+ id-token: write
67
+
68
+ steps:
69
+ - name: Download release distributions
70
+ uses: actions/download-artifact@v4
71
+ with:
72
+ name: python-package-distributions
73
+ path: dist/
74
+
75
+ - name: Publish distributions to PyPI
76
+ uses: pypa/gh-action-pypi-publish@release/v1
77
+ with:
78
+ print-hash: true
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
6
+ intends to follow Semantic Versioning once the public API stabilizes.
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - Added `requirements.txt` for runtime dependencies and `requirements-dev.txt` for development, testing, and packaging dependencies.
13
+
14
+ ### Changed
15
+
16
+ - Removed Ruff from the mandatory CI and release check gate; linting and formatting remain available as optional local commands.
17
+
18
+ ## [0.1.0] - 2026-08-26
19
+
20
+ ### Changed
21
+
22
+ - Flattened the framework integration into `fastpermit.integrations` so framework names no longer appear in internal module paths.
23
+ - Updated package and documentation metadata for the first public release.
24
+
25
+ ### Added
26
+
27
+ - PyPI Trusted Publishing release workflow using GitHub OIDC.
28
+ - Release documentation and tag/version verification.
29
+ - Public repository metadata, maintainer information, CI badges, and security policy.
30
+ - Request-level prechecks for object dependencies so denied requests do not load protected resources.
31
+ - Source and wheel packaging configuration with typed-package marker support.
32
+ - Initial FastPermit package scaffold.
33
+ - Backend-agnostic permission core.
34
+ - Three-state permission evaluation for correct request/object composition.
35
+ - `AND`, `OR`, and `NOT` permission operators.
36
+ - `all_of()` and `any_of()` helpers.
37
+ - `Principal` protocol and `BasicPrincipal` implementation.
38
+ - `PermissionBackend` protocol and `InMemoryBackend`.
39
+ - Built-in `AllowAny`, `DenyAll`, `IsAuthenticated`, `HasRole`, and `HasPermission` rules.
40
+ - Framework `require()` and `require_object()` integrations.
41
+ - Unit and integration test suites.
42
+ - Ruff, mypy, pytest, coverage, build, and GitHub Actions configuration.
@@ -0,0 +1,33 @@
1
+ # Contributing
2
+
3
+ ## Development setup
4
+
5
+ ```bash
6
+ python -m venv .venv
7
+ source .venv/bin/activate
8
+ pip install -e '.[dev]'
9
+ ```
10
+
11
+ Run the complete local quality gate:
12
+
13
+ ```bash
14
+ make check
15
+ ```
16
+
17
+ Individual commands:
18
+
19
+ ```bash
20
+ make lint
21
+ make format-check
22
+ make typecheck
23
+ make test
24
+ make build
25
+ ```
26
+
27
+ ## Pull requests
28
+
29
+ - Keep the permission core independent from storage and authentication implementations.
30
+ - Add tests for every behavior change.
31
+ - Add user-facing changes to `CHANGELOG.md`, newest entries first.
32
+ - Keep public APIs fully typed.
33
+ - Use English for code comments and docstrings.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vitaly Sem
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,6 @@
1
+ include CHANGELOG.md
2
+ include CONTRIBUTING.md
3
+ include LICENSE
4
+ include Makefile
5
+ recursive-include examples *.py
6
+ recursive-include .github *.yml
@@ -0,0 +1,29 @@
1
+ .PHONY: install lint format format-check typecheck test build check clean
2
+
3
+ install:
4
+ python -m pip install -e '.[dev]'
5
+
6
+ lint:
7
+ ruff check .
8
+
9
+ format:
10
+ ruff format .
11
+
12
+ format-check:
13
+ ruff format --check .
14
+
15
+ typecheck:
16
+ mypy src/fastpermit
17
+
18
+ test:
19
+ pytest --cov=fastpermit --cov-report=term-missing
20
+
21
+ build:
22
+ python -m build
23
+ python -m twine check dist/*
24
+
25
+ check: typecheck test build
26
+
27
+ clean:
28
+ rm -rf .coverage .mypy_cache .pytest_cache .ruff_cache build dist htmlcov
29
+ find . -type d -name '__pycache__' -prune -exec rm -rf {} +
@@ -0,0 +1,383 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastpermit
3
+ Version: 0.1.0
4
+ Summary: Composable, backend-agnostic authorization for FastAPI.
5
+ Author: Vitalii Semotiuk
6
+ Maintainer: Vitalii Semotiuk
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/Vir2S/fastpermit
9
+ Project-URL: Repository, https://github.com/Vir2S/fastpermit
10
+ Project-URL: Issues, https://github.com/Vir2S/fastpermit/issues
11
+ Project-URL: Changelog, https://github.com/Vir2S/fastpermit/blob/master/CHANGELOG.md
12
+ Keywords: fastapi,authorization,permissions,rbac,abac,access-control
13
+ Classifier: Development Status :: 2 - Pre-Alpha
14
+ Classifier: Framework :: FastAPI
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.11
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: fastapi>=0.115
27
+ Provides-Extra: dev
28
+ Requires-Dist: build>=1.2; extra == "dev"
29
+ Requires-Dist: httpx>=0.28; extra == "dev"
30
+ Requires-Dist: mypy>=1.15; extra == "dev"
31
+ Requires-Dist: pytest>=8.3; extra == "dev"
32
+ Requires-Dist: pytest-asyncio>=0.25; extra == "dev"
33
+ Requires-Dist: pytest-cov>=6.0; extra == "dev"
34
+ Requires-Dist: ruff>=0.11; extra == "dev"
35
+ Requires-Dist: twine>=6.0; extra == "dev"
36
+ Dynamic: license-file
37
+
38
+ # FastPermit
39
+
40
+ [![CI](https://github.com/Vir2S/fastpermit/actions/workflows/ci.yml/badge.svg)](https://github.com/Vir2S/fastpermit/actions/workflows/ci.yml)
41
+ [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](https://www.python.org/)
42
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
43
+
44
+ **Composable, backend-agnostic authorization for FastAPI.**
45
+
46
+ FastPermit keeps authentication and authorization separate. Your application authenticates a
47
+ principal with JWT, OAuth2, Auth0, Keycloak, FastAPI Users, or a custom mechanism. FastPermit then
48
+ decides whether that principal may perform an action.
49
+
50
+ The core is deliberately small:
51
+
52
+ - composable permissions with `&`, `|`, and `~`;
53
+ - request-level and object-level authorization;
54
+ - role-based access control (RBAC) primitives;
55
+ - pluggable permission backends;
56
+ - async-first execution;
57
+ - FastAPI dependency integration;
58
+ - no ORM or cache dependency in the core.
59
+
60
+ > Status: `0.1.0` — first public release.
61
+
62
+ ## Project status
63
+
64
+ FastPermit is in active early development. The `0.1.x` line focuses on a small, typed,
65
+ backend-agnostic core before adding optional persistence and caching adapters.
66
+
67
+ Planned next steps:
68
+
69
+ - SQLAlchemy 2 / PostgreSQL adapter;
70
+ - Redis permission cache and invalidation hooks;
71
+ - tenant and resource scopes;
72
+ - audit and observability hooks.
73
+
74
+ ## Installation
75
+
76
+ Install from PyPI:
77
+
78
+ ```bash
79
+ pip install fastpermit
80
+ ```
81
+
82
+ For development:
83
+
84
+ ```bash
85
+ git clone https://github.com/Vir2S/fastpermit.git
86
+ cd fastpermit
87
+ python -m venv .venv
88
+ source .venv/bin/activate
89
+ pip install -e '.[dev]'
90
+ make check
91
+ ```
92
+
93
+ ## Quick start
94
+
95
+ ```python
96
+ from typing import Annotated
97
+
98
+ from fastapi import Depends, FastAPI
99
+
100
+ from fastpermit import BasicPrincipal, FastPermit, HasPermission, InMemoryBackend
101
+
102
+ app = FastAPI()
103
+
104
+ backend = InMemoryBackend(
105
+ {
106
+ "user-1": {"project:read", "project:update"},
107
+ }
108
+ )
109
+
110
+
111
+ async def get_current_principal() -> BasicPrincipal:
112
+ # Replace this with JWT, OAuth2, Auth0, Keycloak, or your own authentication.
113
+ return BasicPrincipal(id="user-1", roles=frozenset({"developer"}))
114
+
115
+
116
+ permit = FastPermit(
117
+ backend=backend,
118
+ principal_loader=get_current_principal,
119
+ )
120
+
121
+
122
+ @app.get("/projects")
123
+ async def list_projects(
124
+ principal: Annotated[
125
+ BasicPrincipal,
126
+ Depends(permit.require("project:read")),
127
+ ],
128
+ ) -> dict[str, str]:
129
+ return {"principal_id": str(principal.id)}
130
+ ```
131
+
132
+ A string passed to `require()` is shorthand for `HasPermission(...)`:
133
+
134
+ ```python
135
+ Depends(permit.require("project:read"))
136
+ ```
137
+
138
+ is equivalent to:
139
+
140
+ ```python
141
+ Depends(permit.require(HasPermission("project:read")))
142
+ ```
143
+
144
+ ## Permission algebra
145
+
146
+ Permissions can be combined without putting authorization branches in route handlers:
147
+
148
+ ```python
149
+ from fastpermit import HasPermission, HasRole, IsAuthenticated
150
+
151
+ permission = (
152
+ IsAuthenticated()
153
+ & HasPermission("project:update")
154
+ & (
155
+ HasRole("admin")
156
+ | HasPermission("project:update:any")
157
+ )
158
+ )
159
+ ```
160
+
161
+ Supported operators:
162
+
163
+ ```python
164
+ A() & B() # AND
165
+ A() | B() # OR
166
+ ~A() # NOT
167
+ ```
168
+
169
+ The helpers `all_of()` and `any_of()` are available for larger expressions:
170
+
171
+ ```python
172
+ from fastpermit import all_of, any_of
173
+
174
+ permission = all_of(
175
+ IsAuthenticated(),
176
+ HasPermission("project:update"),
177
+ any_of(
178
+ HasRole("admin"),
179
+ HasRole("manager"),
180
+ ),
181
+ )
182
+ ```
183
+
184
+ ## Object-level permissions
185
+
186
+ Object rules are intentionally separate from loading the object. A custom rule only needs to
187
+ implement `has_object_permission()`:
188
+
189
+ ```python
190
+ from typing import Any
191
+
192
+ from fastpermit import BasePermission, PermissionContext, Principal
193
+
194
+
195
+ class IsOwner(BasePermission):
196
+ async def has_object_permission(
197
+ self,
198
+ principal: Principal | None,
199
+ obj: Any,
200
+ context: PermissionContext,
201
+ ) -> bool:
202
+ return principal is not None and obj.owner_id == principal.id
203
+ ```
204
+
205
+ Then combine it with ordinary permissions:
206
+
207
+ ```python
208
+ edit_project = (
209
+ HasPermission("project:update:any")
210
+ | (
211
+ HasPermission("project:update")
212
+ & IsOwner()
213
+ )
214
+ )
215
+ ```
216
+
217
+ Use it with a FastAPI loader dependency:
218
+
219
+ ```python
220
+ @app.patch("/projects/{project_id}")
221
+ async def update_project(
222
+ project=Depends(
223
+ permit.require_object(
224
+ edit_project,
225
+ loader=get_project,
226
+ )
227
+ ),
228
+ ):
229
+ return project
230
+ ```
231
+
232
+ FastPermit evaluates both request-level and object-level branches as a single expression. Rules
233
+ that do not apply during a phase are neutral rather than implicitly allowing or denying it. This
234
+ keeps expressions such as `HasRole("admin") | IsOwner()` and `~IsOwner()` logically correct.
235
+
236
+ ## Principals
237
+
238
+ FastPermit uses a small `Principal` protocol rather than a concrete user model. The included
239
+ `BasicPrincipal` is convenient for most applications:
240
+
241
+ ```python
242
+ from fastpermit import BasicPrincipal
243
+
244
+ principal = BasicPrincipal(
245
+ id="user-42",
246
+ roles=frozenset({"manager", "reviewer"}),
247
+ attributes={"organization_id": "org-1"},
248
+ )
249
+ ```
250
+
251
+ You may return your own object from the authentication dependency as long as it exposes:
252
+
253
+ ```text
254
+ id
255
+ roles
256
+ attributes
257
+ is_authenticated
258
+ ```
259
+
260
+ ## Backends
261
+
262
+ A backend answers one question: which permission codes are effective for this principal in this
263
+ scope?
264
+
265
+ ```python
266
+ from collections.abc import Mapping
267
+ from typing import AbstractSet, Any
268
+
269
+ from fastpermit import PermissionBackend, Principal
270
+
271
+
272
+ class MyBackend(PermissionBackend):
273
+ async def get_permissions(
274
+ self,
275
+ principal: Principal,
276
+ *,
277
+ scope: Mapping[str, Any],
278
+ ) -> AbstractSet[str]:
279
+ ...
280
+ ```
281
+
282
+ `InMemoryBackend` is included for tests, prototypes, and examples. SQLAlchemy/PostgreSQL and Redis
283
+ adapters are intentionally planned as optional integrations instead of core requirements.
284
+
285
+ ## Request context and scopes
286
+
287
+ A permission receives `PermissionContext`, which contains:
288
+
289
+ - the configured backend;
290
+ - a scope mapping;
291
+ - integration-specific attributes;
292
+ - a per-evaluation permission cache.
293
+
294
+ FastAPI integration exposes the current `Request` as `context.attributes["request"]` without
295
+ making the authorization core depend on FastAPI.
296
+
297
+ Static scope can be attached to a dependency:
298
+
299
+ ```python
300
+ Depends(
301
+ permit.require(
302
+ "billing:read",
303
+ scope={"tenant": "global"},
304
+ )
305
+ )
306
+ ```
307
+
308
+ Dynamic tenant scopes are planned for the next integration iteration.
309
+
310
+ ## HTTP semantics
311
+
312
+ FastPermit does not authenticate requests. Authentication remains the responsibility of your
313
+ principal loader.
314
+
315
+ When a FastPermit rule denies access:
316
+
317
+ - an absent or unauthenticated principal produces `401 Unauthorized`;
318
+ - an authenticated principal without sufficient authorization produces `403 Forbidden`.
319
+
320
+ ## Design principles
321
+
322
+ 1. Authentication and authorization are separate concerns.
323
+ 2. Routes should describe required access, not implement role branches.
324
+ 3. Permission codes are stable capabilities such as `project:update`.
325
+ 4. Roles aggregate capabilities; application code should not be coupled to role names where a
326
+ capability is the real requirement.
327
+ 5. Object-level rules belong in permissions, not route handlers.
328
+ 6. Storage and caching are adapters, not core concerns.
329
+ 7. Authorization expressions must preserve correct semantics across request and object phases.
330
+
331
+ ## Roadmap
332
+
333
+ ### 0.1
334
+
335
+ - [x] permission core;
336
+ - [x] `AND`, `OR`, `NOT` composition;
337
+ - [x] `all_of()` / `any_of()`;
338
+ - [x] `IsAuthenticated`;
339
+ - [x] `HasRole`;
340
+ - [x] `HasPermission`;
341
+ - [x] object-level permissions;
342
+ - [x] backend protocol;
343
+ - [x] in-memory backend;
344
+ - [x] FastAPI integration;
345
+ - [x] typed package;
346
+ - [x] tests and CI.
347
+
348
+ ### 0.2
349
+
350
+ - [ ] SQLAlchemy 2.x adapter;
351
+ - [ ] PostgreSQL RBAC reference models;
352
+ - [ ] Alembic examples;
353
+ - [ ] user-role and role-permission repositories.
354
+
355
+ ### 0.3
356
+
357
+ - [ ] Redis cache adapter;
358
+ - [ ] cache invalidation primitives;
359
+ - [ ] cache versioning;
360
+ - [ ] configurable TTL policies.
361
+
362
+ ### 0.4
363
+
364
+ - [ ] dynamic tenant scopes;
365
+ - [ ] attribute-based access control helpers;
366
+ - [ ] resource scopes;
367
+ - [ ] policy metadata.
368
+
369
+ ### 0.5
370
+
371
+ - [ ] authorization audit events;
372
+ - [ ] observability hooks;
373
+ - [ ] OpenTelemetry integration.
374
+
375
+ ## License
376
+
377
+ MIT
378
+
379
+ ## Maintainer
380
+
381
+ Created and maintained by [Vitaly Sem](https://github.com/Vir2S).
382
+
383
+ FastPermit is an independent open-source project developed with support from Born2CodeLab.