fastapi-rbac-authz 0.2.0__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.
@@ -0,0 +1,269 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi-rbac-authz
3
+ Version: 0.2.0
4
+ Summary: Role-based access control with contextual authorization for FastAPI
5
+ Project-URL: Homepage, https://github.com/parikls/fastapi-rbac
6
+ Author-email: Dmytro Smyk <porovozls@gmail.com>
7
+ License-Expression: MIT
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Framework :: FastAPI
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Security
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: <4,>=3.12
19
+ Requires-Dist: fastapi>=0.100.0
20
+ Requires-Dist: starlette>=0.27.0
21
+ Description-Content-Type: text/markdown
22
+
23
+ # fastapi-rbac-authz
24
+
25
+ Role-based access control with contextual authorization for FastAPI.
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ pip install fastapi-rbac-authz
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ ```python
36
+ from typing import Annotated
37
+ from fastapi import Depends, FastAPI
38
+ from fastapi_rbac import (
39
+ RBACAuthz, RBACRouter, Global, Contextual, ContextualAuthz, RBACUser
40
+ )
41
+
42
+ # 1. Define your user model
43
+ class User:
44
+ def __init__(self, user_id: str, roles: set[str]):
45
+ self.user_id = user_id
46
+ self.roles = roles
47
+
48
+ # 2. Define role permissions
49
+ PERMISSIONS = {
50
+ "admin": {
51
+ Global("report:*"), # Admin can do anything with reports
52
+ },
53
+ "viewer": {
54
+ Contextual("report:read"), # Viewer needs context check
55
+ },
56
+ }
57
+
58
+ # 3. Create a context check (for contextual permissions)
59
+ # All __init__ params are injected by FastAPI's DI system
60
+ class ReportAccessContext(ContextualAuthz[User]):
61
+ def __init__(
62
+ self,
63
+ report_id: int, # <-- Injected from path parameter
64
+ user: Annotated[User, Depends(RBACUser)],
65
+ ):
66
+ self.user = user
67
+ self.report_id = report_id
68
+
69
+ async def has_permissions(self) -> bool:
70
+ # Your logic: check if user can access this specific report
71
+ allowed_reports = {1, 2, 3} # e.g., query from database
72
+ return self.report_id in allowed_reports
73
+
74
+ # 4. Create your app and configure RBAC
75
+ app = FastAPI()
76
+
77
+ async def get_current_user() -> User:
78
+ # Your authentication logic here
79
+ return User(user_id="user-1", roles={"viewer"})
80
+
81
+ RBACAuthz(
82
+ app,
83
+ get_roles=lambda u: u.roles,
84
+ permissions=PERMISSIONS,
85
+ user_dependency=get_current_user,
86
+ ui_path="/_rbac", # Optional: mount visualization UI
87
+ )
88
+
89
+ # 5. Create protected routes
90
+ router = RBACRouter(permissions={"report:read"}, contexts=[ReportAccessContext])
91
+
92
+ @router.get("/reports/{report_id}")
93
+ async def get_report(report_id: int):
94
+ return {"report_id": report_id}
95
+
96
+ @router.post("/reports", permissions={"report:create"}) # Override permissions
97
+ async def create_report():
98
+ return {"id": "new-report"}
99
+
100
+ app.include_router(router, prefix="/api")
101
+ ```
102
+
103
+ ## Permission Scopes
104
+
105
+ Permissions can be granted with two scopes:
106
+
107
+ ### Global Scope
108
+
109
+ ```python
110
+ Global("report:read")
111
+ ```
112
+
113
+ Global permissions **bypass context checks entirely**. If a user has a global permission, they can access the resource without any additional validation. Use this for admin roles or service accounts that need unrestricted access.
114
+
115
+ ### Contextual Scope
116
+
117
+ ```python
118
+ Contextual("report:read")
119
+ ```
120
+
121
+ Contextual permissions **require context checks to pass**. The user must have the permission AND the context check must return `True`. Use this for regular users who should only access resources they own or are members of.
122
+
123
+ ### Example
124
+
125
+ ```python
126
+ PERMISSIONS = {
127
+ "admin": {
128
+ Global("report:*"), # Can access ALL reports, no questions asked
129
+ },
130
+ "user": {
131
+ Contextual("report:read"), # Can only read reports they have access to
132
+ Contextual("report:create"),
133
+ },
134
+ }
135
+ ```
136
+
137
+ ## Context Checks
138
+
139
+ Context checks are classes that implement fine-grained authorization logic. They're regular FastAPI dependencies, so you can inject any parameters (path params, query params, request body, database sessions, etc.).
140
+
141
+ ```python
142
+ class ReportAccessContext(ContextualAuthz[User]):
143
+ def __init__(
144
+ self,
145
+ report_id: int, # Injected from path parameter
146
+ user: Annotated[User, Depends(RBACUser)],
147
+ db: Annotated[AsyncSession, Depends(get_db)], # Database session
148
+ ):
149
+ self.user = user
150
+ self.report_id = report_id
151
+ self.db = db
152
+
153
+ async def has_permissions(self) -> bool:
154
+ # Query database to check if user can access this report
155
+ report = await self.db.get(Report, self.report_id)
156
+ return report is not None and report.owner_id == self.user.user_id
157
+ ```
158
+
159
+ ## Authorization Flow
160
+
161
+ When a request hits an RBAC-protected endpoint:
162
+
163
+ ```
164
+ 1. Authentication
165
+ └── user_dependency runs → User object available
166
+
167
+ 2. Permission Check
168
+ └── Does user have ANY grant (global or contextual) for required permission?
169
+ ├── No → 403 Forbidden
170
+ └── Yes → Continue
171
+
172
+ 3. Scope Evaluation
173
+ └── Is the grant Global?
174
+ ├── Yes → Access granted (skip context checks)
175
+ └── No → Continue to context checks
176
+
177
+ 4. Context Checks (only for Contextual grants)
178
+ └── Run all context classes via FastAPI DI
179
+ └── Do ALL contexts return True?
180
+ ├── No → 403 Forbidden
181
+ └── Yes → Access granted
182
+
183
+ 5. Endpoint Handler Executes
184
+ ```
185
+
186
+ ## Wildcard Permissions
187
+
188
+ Use wildcards to grant multiple permissions at once:
189
+
190
+ | Grant | Implies |
191
+ |-------|---------|
192
+ | `*` | Everything |
193
+ | `report:*` | `report:read`, `report:create`, `report:delete`, etc. |
194
+ | `report:metrics:*` | `report:metrics:view`, `report:metrics:export`, etc. |
195
+
196
+ ```python
197
+ PERMISSIONS = {
198
+ "admin": {Global("*")}, # Full access to everything
199
+ "reporter": {Global("report:*")}, # Full access to reports only
200
+ }
201
+ ```
202
+
203
+ ## Running the Example
204
+
205
+ A complete runnable example is included in the `examples/` directory:
206
+
207
+ ```bash
208
+ # Install dependencies
209
+ pip install fastapi-rbac-authz uvicorn
210
+
211
+ # Run the example
212
+ uvicorn examples.basic_app:app --reload
213
+ ```
214
+
215
+ Then open your browser:
216
+
217
+ - **http://localhost:8000/docs** - OpenAPI docs to test the API
218
+ - **http://localhost:8000/_rbac** - Authorization visualization UI
219
+
220
+ ### Test with different users
221
+
222
+ The example uses `X-Token` header for authentication:
223
+
224
+ ```bash
225
+ # As admin (has Global("*") - full access)
226
+ curl -H "X-Token: admin-token" http://localhost:8000/reports
227
+ curl -H "X-Token: admin-token" http://localhost:8000/reports/1
228
+
229
+ # As user (has Contextual permissions - can only access own reports)
230
+ curl -H "X-Token: user-token" http://localhost:8000/reports/1 # OK (owns report 1)
231
+ curl -H "X-Token: user-token" http://localhost:8000/reports/3 # 403 (doesn't own report 3)
232
+
233
+ # As viewer (has Contextual read - can only read own reports)
234
+ curl -H "X-Token: viewer-token" http://localhost:8000/reports/1 # 403 (doesn't own any)
235
+ ```
236
+
237
+ ## Visualization UI
238
+
239
+ Mount the built-in visualization UI to explore your authorization schema:
240
+
241
+ ```python
242
+ RBACAuthz(
243
+ app,
244
+ # ...
245
+ ui_path="/_rbac",
246
+ )
247
+ ```
248
+
249
+ Visit `/_rbac` to see an interactive graph showing:
250
+ - **Roles** and their permission grants
251
+ - **Permissions** with scope indicators (global/contextual)
252
+ - **Endpoints** and their required permissions
253
+ - **Context checks** and which endpoints use them
254
+
255
+ Double-click any node to isolate and explore its relationships.
256
+
257
+ ### Screenshots
258
+
259
+ **Full authorization graph**
260
+
261
+ ![Full Graph](https://github.com/user-attachments/assets/912826a4-63a6-4865-8ac7-707ce2746033)
262
+
263
+ **Role isolation view** - double-click a role to see what it can access
264
+
265
+ ![Role Isolation](https://github.com/user-attachments/assets/392b8d06-aa10-433a-a7bf-bce1af02d9df)
266
+
267
+ **Endpoint isolation view** - double-click an endpoint to see who can access it
268
+
269
+ ![Endpoint Isolation](https://github.com/user-attachments/assets/b0357fe1-d265-4aed-a4e3-ee439a370c46)
@@ -0,0 +1,15 @@
1
+ fastapi_rbac/__init__.py,sha256=pmId7xpntLKSwaQaFmBwJa443-FyfGOFwl_pf8t04sE,817
2
+ fastapi_rbac/context.py,sha256=txZqnw8xGPHsE3DQi75Q7ny_VifT0O2etcz5E8sm2wE,1070
3
+ fastapi_rbac/core.py,sha256=smTSbxCQYyMBYvkzOavPurIMWKXrp1ZHWXOs7WVwVvY,4154
4
+ fastapi_rbac/dependencies.py,sha256=2475jW8QPRUWubA__M48D-EY8GbL2SJH8mLaaEMoLfg,9325
5
+ fastapi_rbac/exceptions.py,sha256=tSFbes6yzAygWXXl9NLdZ5K2_iISiJ6ZGJCD6077i94,244
6
+ fastapi_rbac/permissions.py,sha256=VReUjewdFehkDpA_rowSAV2qFoAfoDhrLvoU2gGSxww,2705
7
+ fastapi_rbac/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
8
+ fastapi_rbac/router.py,sha256=kLUmWmBZKqNoGvoD44g-Un9tCIO_dP3psQYrNBm5QFI,11405
9
+ fastapi_rbac/ui/__init__.py,sha256=u2enI_c9k1d0T0z1kBuGI4lpR0wifqFzkfaEUEnuLXg,220
10
+ fastapi_rbac/ui/routes.py,sha256=d57_bPsrpFePzumqcHIU3Okf8TfXKkqf8KLhkrvCg_I,2081
11
+ fastapi_rbac/ui/schema.py,sha256=p3mWgkpA20jYI_g7KiIdULXCFzSeNXBMIL9c7Pe7Zgg,7917
12
+ fastapi_rbac/ui/static/index.html,sha256=HUxI7eO84duCxxOi9GqP4yybA6QrxQAbNcc6lYkjM30,73455
13
+ fastapi_rbac_authz-0.2.0.dist-info/METADATA,sha256=EPHDOLeaGOgDBFWyDzEpgowV1DlFgx3HhJXVP5YBSTI,7985
14
+ fastapi_rbac_authz-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
15
+ fastapi_rbac_authz-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any