fastapi-rbac-authz 0.4.0__tar.gz → 0.5.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.
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/PKG-INFO +25 -20
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/README.md +24 -19
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/pyproject.toml +1 -1
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/.github/workflows/checks.yaml +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/.github/workflows/pypi-minor-deployment.yaml +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/.gitignore +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/.pre-commit-config.yaml +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/CLAUDE.md +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/examples/basic_app.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/fastapi_rbac/__init__.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/fastapi_rbac/context.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/fastapi_rbac/core.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/fastapi_rbac/dependencies.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/fastapi_rbac/errors.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/fastapi_rbac/permissions.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/fastapi_rbac/py.typed +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/fastapi_rbac/router.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/fastapi_rbac/ui/__init__.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/fastapi_rbac/ui/routes.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/fastapi_rbac/ui/schema.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/fastapi_rbac/ui/static/index.html +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/poetry.lock +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/tests/__init__.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/tests/conftest.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/tests/test_context.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/tests/test_context_di.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/tests/test_core.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/tests/test_dependencies.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/tests/test_exceptions.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/tests/test_integration.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/tests/test_permissions.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/tests/test_router.py +0 -0
- {fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/tests/test_ui.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-rbac-authz
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Role-based access control with contextual authorization for FastAPI
|
|
5
5
|
Project-URL: Homepage, https://github.com/parikls/fastapi-rbac
|
|
6
6
|
Author-email: Dmytro Smyk <porovozls@gmail.com>
|
|
@@ -24,6 +24,14 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
|
|
25
25
|
Role-based access control with contextual authorization for FastAPI.
|
|
26
26
|
|
|
27
|
+
Library IS NOT responsible for authentication. You can use any authentication mechanism you want.
|
|
28
|
+
|
|
29
|
+
What you need to provide:
|
|
30
|
+
- A dependency that returns the authenticated user's roles as `set[str]`
|
|
31
|
+
- Permission definitions per role
|
|
32
|
+
- Use `RBACRouter` instead of `APIRouter` for protected routes
|
|
33
|
+
- Define permissions and context checks per endpoint
|
|
34
|
+
|
|
27
35
|
## Installation
|
|
28
36
|
|
|
29
37
|
```bash
|
|
@@ -39,23 +47,23 @@ from fastapi_rbac import (
|
|
|
39
47
|
RBACAuthz, RBACRouter, Global, Contextual, ContextualAuthz
|
|
40
48
|
)
|
|
41
49
|
|
|
42
|
-
# 1.
|
|
50
|
+
# 1. You might have your user model (or you may not, we don't care)
|
|
43
51
|
class User:
|
|
44
52
|
def __init__(self, user_id: str, roles: set[str]):
|
|
45
53
|
self.user_id = user_id
|
|
46
54
|
self.roles = roles
|
|
47
55
|
|
|
48
|
-
# 2.
|
|
49
|
-
# The library only needs roles - you can authenticate however you like
|
|
56
|
+
# 2. Assuming you have your own dependency that returns a user instance
|
|
50
57
|
async def get_current_user() -> User:
|
|
51
58
|
# Your authentication logic here
|
|
52
59
|
return User(user_id="user-1", roles={"viewer"})
|
|
53
60
|
|
|
61
|
+
# 3. Dependency that library **REQUIRES**. **MUST** return a set of user roles
|
|
54
62
|
async def get_current_user_roles() -> set[str]:
|
|
55
63
|
user = await get_current_user()
|
|
56
64
|
return user.roles
|
|
57
65
|
|
|
58
|
-
#
|
|
66
|
+
# 4. Define your roles permissions
|
|
59
67
|
PERMISSIONS = {
|
|
60
68
|
"admin": {
|
|
61
69
|
Global("report:*"), # Admin can do anything with reports
|
|
@@ -65,13 +73,12 @@ PERMISSIONS = {
|
|
|
65
73
|
},
|
|
66
74
|
}
|
|
67
75
|
|
|
68
|
-
#
|
|
69
|
-
# Context classes are responsible for their own authentication via FastAPI DI
|
|
76
|
+
# 5. Create context authorization checks
|
|
70
77
|
class ReportAccessContext(ContextualAuthz):
|
|
71
78
|
def __init__(
|
|
72
79
|
self,
|
|
73
80
|
report_id: int, # <-- Injected from path parameter
|
|
74
|
-
user: Annotated[User, Depends(get_current_user)], # Your
|
|
81
|
+
user: Annotated[User, Depends(get_current_user)], # Your own way how to get the user if you need it
|
|
75
82
|
):
|
|
76
83
|
self.user = user
|
|
77
84
|
self.report_id = report_id
|
|
@@ -81,7 +88,7 @@ class ReportAccessContext(ContextualAuthz):
|
|
|
81
88
|
allowed_reports = {1, 2, 3} # e.g., query from database
|
|
82
89
|
return self.report_id in allowed_reports
|
|
83
90
|
|
|
84
|
-
#
|
|
91
|
+
# 6. Configure RBAC
|
|
85
92
|
app = FastAPI()
|
|
86
93
|
|
|
87
94
|
RBACAuthz(
|
|
@@ -91,7 +98,7 @@ RBACAuthz(
|
|
|
91
98
|
ui_path="/_rbac", # Optional: mount visualization UI
|
|
92
99
|
)
|
|
93
100
|
|
|
94
|
-
#
|
|
101
|
+
# 7. Create protected routes
|
|
95
102
|
router = RBACRouter(permissions={"report:read"}, contexts=[ReportAccessContext])
|
|
96
103
|
|
|
97
104
|
@router.get("/reports/{report_id}")
|
|
@@ -105,8 +112,6 @@ async def create_report():
|
|
|
105
112
|
app.include_router(router, prefix="/api")
|
|
106
113
|
```
|
|
107
114
|
|
|
108
|
-
> **Note:** The library doesn't care how you authenticate - whether via dependency, middleware, JWT decode, or any other method. You just need to provide a dependency that returns the user's roles as `set[str]`. Context classes are responsible for their own authentication and can use any FastAPI dependency pattern.
|
|
109
|
-
|
|
110
115
|
## Permission Scopes
|
|
111
116
|
|
|
112
117
|
Permissions can be granted with two scopes:
|
|
@@ -172,7 +177,7 @@ When a request hits an RBAC-protected endpoint:
|
|
|
172
177
|
└── roles_dependency runs → User's roles (set[str]) available
|
|
173
178
|
|
|
174
179
|
2. Permission Check
|
|
175
|
-
└── Does user have ANY grant (
|
|
180
|
+
└── Does user have ANY grant (scoped or wildcard) for required permission?
|
|
176
181
|
├── No → 403 Forbidden
|
|
177
182
|
└── Yes → Continue
|
|
178
183
|
|
|
@@ -221,8 +226,8 @@ uvicorn examples.basic_app:app --reload
|
|
|
221
226
|
|
|
222
227
|
Then open your browser:
|
|
223
228
|
|
|
224
|
-
- **http://localhost:
|
|
225
|
-
- **http://localhost:
|
|
229
|
+
- **http://localhost:18000/docs** - OpenAPI docs to test the API
|
|
230
|
+
- **http://localhost:18000/_rbac** - Authorization visualization UI
|
|
226
231
|
|
|
227
232
|
### Test with different users
|
|
228
233
|
|
|
@@ -230,15 +235,15 @@ The example uses `X-Token` header for authentication:
|
|
|
230
235
|
|
|
231
236
|
```bash
|
|
232
237
|
# As admin (has Global("*") - full access)
|
|
233
|
-
curl -H "X-Token: admin-token" http://localhost:
|
|
234
|
-
curl -H "X-Token: admin-token" http://localhost:
|
|
238
|
+
curl -H "X-Token: admin-token" http://localhost:18000/reports
|
|
239
|
+
curl -H "X-Token: admin-token" http://localhost:18000/reports/1
|
|
235
240
|
|
|
236
241
|
# As user (has Contextual permissions - can only access own reports)
|
|
237
|
-
curl -H "X-Token: user-token" http://localhost:
|
|
238
|
-
curl -H "X-Token: user-token" http://localhost:
|
|
242
|
+
curl -H "X-Token: user-token" http://localhost:18000/reports/1 # OK (owns report 1)
|
|
243
|
+
curl -H "X-Token: user-token" http://localhost:18000/reports/3 # 403 (doesn't own report 3)
|
|
239
244
|
|
|
240
245
|
# As viewer (has Contextual read - can only read own reports)
|
|
241
|
-
curl -H "X-Token: viewer-token" http://localhost:
|
|
246
|
+
curl -H "X-Token: viewer-token" http://localhost:18000/reports/1 # 403 (doesn't own any)
|
|
242
247
|
```
|
|
243
248
|
|
|
244
249
|
## Visualization UI
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Role-based access control with contextual authorization for FastAPI.
|
|
4
4
|
|
|
5
|
+
Library IS NOT responsible for authentication. You can use any authentication mechanism you want.
|
|
6
|
+
|
|
7
|
+
What you need to provide:
|
|
8
|
+
- A dependency that returns the authenticated user's roles as `set[str]`
|
|
9
|
+
- Permission definitions per role
|
|
10
|
+
- Use `RBACRouter` instead of `APIRouter` for protected routes
|
|
11
|
+
- Define permissions and context checks per endpoint
|
|
12
|
+
|
|
5
13
|
## Installation
|
|
6
14
|
|
|
7
15
|
```bash
|
|
@@ -17,23 +25,23 @@ from fastapi_rbac import (
|
|
|
17
25
|
RBACAuthz, RBACRouter, Global, Contextual, ContextualAuthz
|
|
18
26
|
)
|
|
19
27
|
|
|
20
|
-
# 1.
|
|
28
|
+
# 1. You might have your user model (or you may not, we don't care)
|
|
21
29
|
class User:
|
|
22
30
|
def __init__(self, user_id: str, roles: set[str]):
|
|
23
31
|
self.user_id = user_id
|
|
24
32
|
self.roles = roles
|
|
25
33
|
|
|
26
|
-
# 2.
|
|
27
|
-
# The library only needs roles - you can authenticate however you like
|
|
34
|
+
# 2. Assuming you have your own dependency that returns a user instance
|
|
28
35
|
async def get_current_user() -> User:
|
|
29
36
|
# Your authentication logic here
|
|
30
37
|
return User(user_id="user-1", roles={"viewer"})
|
|
31
38
|
|
|
39
|
+
# 3. Dependency that library **REQUIRES**. **MUST** return a set of user roles
|
|
32
40
|
async def get_current_user_roles() -> set[str]:
|
|
33
41
|
user = await get_current_user()
|
|
34
42
|
return user.roles
|
|
35
43
|
|
|
36
|
-
#
|
|
44
|
+
# 4. Define your roles permissions
|
|
37
45
|
PERMISSIONS = {
|
|
38
46
|
"admin": {
|
|
39
47
|
Global("report:*"), # Admin can do anything with reports
|
|
@@ -43,13 +51,12 @@ PERMISSIONS = {
|
|
|
43
51
|
},
|
|
44
52
|
}
|
|
45
53
|
|
|
46
|
-
#
|
|
47
|
-
# Context classes are responsible for their own authentication via FastAPI DI
|
|
54
|
+
# 5. Create context authorization checks
|
|
48
55
|
class ReportAccessContext(ContextualAuthz):
|
|
49
56
|
def __init__(
|
|
50
57
|
self,
|
|
51
58
|
report_id: int, # <-- Injected from path parameter
|
|
52
|
-
user: Annotated[User, Depends(get_current_user)], # Your
|
|
59
|
+
user: Annotated[User, Depends(get_current_user)], # Your own way how to get the user if you need it
|
|
53
60
|
):
|
|
54
61
|
self.user = user
|
|
55
62
|
self.report_id = report_id
|
|
@@ -59,7 +66,7 @@ class ReportAccessContext(ContextualAuthz):
|
|
|
59
66
|
allowed_reports = {1, 2, 3} # e.g., query from database
|
|
60
67
|
return self.report_id in allowed_reports
|
|
61
68
|
|
|
62
|
-
#
|
|
69
|
+
# 6. Configure RBAC
|
|
63
70
|
app = FastAPI()
|
|
64
71
|
|
|
65
72
|
RBACAuthz(
|
|
@@ -69,7 +76,7 @@ RBACAuthz(
|
|
|
69
76
|
ui_path="/_rbac", # Optional: mount visualization UI
|
|
70
77
|
)
|
|
71
78
|
|
|
72
|
-
#
|
|
79
|
+
# 7. Create protected routes
|
|
73
80
|
router = RBACRouter(permissions={"report:read"}, contexts=[ReportAccessContext])
|
|
74
81
|
|
|
75
82
|
@router.get("/reports/{report_id}")
|
|
@@ -83,8 +90,6 @@ async def create_report():
|
|
|
83
90
|
app.include_router(router, prefix="/api")
|
|
84
91
|
```
|
|
85
92
|
|
|
86
|
-
> **Note:** The library doesn't care how you authenticate - whether via dependency, middleware, JWT decode, or any other method. You just need to provide a dependency that returns the user's roles as `set[str]`. Context classes are responsible for their own authentication and can use any FastAPI dependency pattern.
|
|
87
|
-
|
|
88
93
|
## Permission Scopes
|
|
89
94
|
|
|
90
95
|
Permissions can be granted with two scopes:
|
|
@@ -150,7 +155,7 @@ When a request hits an RBAC-protected endpoint:
|
|
|
150
155
|
└── roles_dependency runs → User's roles (set[str]) available
|
|
151
156
|
|
|
152
157
|
2. Permission Check
|
|
153
|
-
└── Does user have ANY grant (
|
|
158
|
+
└── Does user have ANY grant (scoped or wildcard) for required permission?
|
|
154
159
|
├── No → 403 Forbidden
|
|
155
160
|
└── Yes → Continue
|
|
156
161
|
|
|
@@ -199,8 +204,8 @@ uvicorn examples.basic_app:app --reload
|
|
|
199
204
|
|
|
200
205
|
Then open your browser:
|
|
201
206
|
|
|
202
|
-
- **http://localhost:
|
|
203
|
-
- **http://localhost:
|
|
207
|
+
- **http://localhost:18000/docs** - OpenAPI docs to test the API
|
|
208
|
+
- **http://localhost:18000/_rbac** - Authorization visualization UI
|
|
204
209
|
|
|
205
210
|
### Test with different users
|
|
206
211
|
|
|
@@ -208,15 +213,15 @@ The example uses `X-Token` header for authentication:
|
|
|
208
213
|
|
|
209
214
|
```bash
|
|
210
215
|
# As admin (has Global("*") - full access)
|
|
211
|
-
curl -H "X-Token: admin-token" http://localhost:
|
|
212
|
-
curl -H "X-Token: admin-token" http://localhost:
|
|
216
|
+
curl -H "X-Token: admin-token" http://localhost:18000/reports
|
|
217
|
+
curl -H "X-Token: admin-token" http://localhost:18000/reports/1
|
|
213
218
|
|
|
214
219
|
# As user (has Contextual permissions - can only access own reports)
|
|
215
|
-
curl -H "X-Token: user-token" http://localhost:
|
|
216
|
-
curl -H "X-Token: user-token" http://localhost:
|
|
220
|
+
curl -H "X-Token: user-token" http://localhost:18000/reports/1 # OK (owns report 1)
|
|
221
|
+
curl -H "X-Token: user-token" http://localhost:18000/reports/3 # 403 (doesn't own report 3)
|
|
217
222
|
|
|
218
223
|
# As viewer (has Contextual read - can only read own reports)
|
|
219
|
-
curl -H "X-Token: viewer-token" http://localhost:
|
|
224
|
+
curl -H "X-Token: viewer-token" http://localhost:18000/reports/1 # 403 (doesn't own any)
|
|
220
225
|
```
|
|
221
226
|
|
|
222
227
|
## Visualization UI
|
|
File without changes
|
{fastapi_rbac_authz-0.4.0 → fastapi_rbac_authz-0.5.0}/.github/workflows/pypi-minor-deployment.yaml
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|