django-bolt 0.1.0__cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.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.
Potentially problematic release.
This version of django-bolt might be problematic. Click here for more details.
- django_bolt/__init__.py +147 -0
- django_bolt/_core.abi3.so +0 -0
- django_bolt/admin/__init__.py +25 -0
- django_bolt/admin/admin_detection.py +179 -0
- django_bolt/admin/asgi_bridge.py +267 -0
- django_bolt/admin/routes.py +91 -0
- django_bolt/admin/static.py +155 -0
- django_bolt/admin/static_routes.py +111 -0
- django_bolt/api.py +1011 -0
- django_bolt/apps.py +7 -0
- django_bolt/async_collector.py +228 -0
- django_bolt/auth/README.md +464 -0
- django_bolt/auth/REVOCATION_EXAMPLE.md +391 -0
- django_bolt/auth/__init__.py +84 -0
- django_bolt/auth/backends.py +236 -0
- django_bolt/auth/guards.py +224 -0
- django_bolt/auth/jwt_utils.py +212 -0
- django_bolt/auth/revocation.py +286 -0
- django_bolt/auth/token.py +335 -0
- django_bolt/binding.py +363 -0
- django_bolt/bootstrap.py +77 -0
- django_bolt/cli.py +133 -0
- django_bolt/compression.py +104 -0
- django_bolt/decorators.py +159 -0
- django_bolt/dependencies.py +128 -0
- django_bolt/error_handlers.py +305 -0
- django_bolt/exceptions.py +294 -0
- django_bolt/health.py +129 -0
- django_bolt/logging/__init__.py +6 -0
- django_bolt/logging/config.py +357 -0
- django_bolt/logging/middleware.py +296 -0
- django_bolt/management/__init__.py +1 -0
- django_bolt/management/commands/__init__.py +0 -0
- django_bolt/management/commands/runbolt.py +427 -0
- django_bolt/middleware/__init__.py +32 -0
- django_bolt/middleware/compiler.py +131 -0
- django_bolt/middleware/middleware.py +247 -0
- django_bolt/openapi/__init__.py +23 -0
- django_bolt/openapi/config.py +196 -0
- django_bolt/openapi/plugins.py +439 -0
- django_bolt/openapi/routes.py +152 -0
- django_bolt/openapi/schema_generator.py +581 -0
- django_bolt/openapi/spec/__init__.py +68 -0
- django_bolt/openapi/spec/base.py +74 -0
- django_bolt/openapi/spec/callback.py +24 -0
- django_bolt/openapi/spec/components.py +72 -0
- django_bolt/openapi/spec/contact.py +21 -0
- django_bolt/openapi/spec/discriminator.py +25 -0
- django_bolt/openapi/spec/encoding.py +67 -0
- django_bolt/openapi/spec/enums.py +41 -0
- django_bolt/openapi/spec/example.py +36 -0
- django_bolt/openapi/spec/external_documentation.py +21 -0
- django_bolt/openapi/spec/header.py +132 -0
- django_bolt/openapi/spec/info.py +50 -0
- django_bolt/openapi/spec/license.py +28 -0
- django_bolt/openapi/spec/link.py +66 -0
- django_bolt/openapi/spec/media_type.py +51 -0
- django_bolt/openapi/spec/oauth_flow.py +36 -0
- django_bolt/openapi/spec/oauth_flows.py +28 -0
- django_bolt/openapi/spec/open_api.py +87 -0
- django_bolt/openapi/spec/operation.py +105 -0
- django_bolt/openapi/spec/parameter.py +147 -0
- django_bolt/openapi/spec/path_item.py +78 -0
- django_bolt/openapi/spec/paths.py +27 -0
- django_bolt/openapi/spec/reference.py +38 -0
- django_bolt/openapi/spec/request_body.py +38 -0
- django_bolt/openapi/spec/response.py +48 -0
- django_bolt/openapi/spec/responses.py +44 -0
- django_bolt/openapi/spec/schema.py +678 -0
- django_bolt/openapi/spec/security_requirement.py +28 -0
- django_bolt/openapi/spec/security_scheme.py +69 -0
- django_bolt/openapi/spec/server.py +34 -0
- django_bolt/openapi/spec/server_variable.py +32 -0
- django_bolt/openapi/spec/tag.py +32 -0
- django_bolt/openapi/spec/xml.py +44 -0
- django_bolt/pagination.py +669 -0
- django_bolt/param_functions.py +49 -0
- django_bolt/params.py +337 -0
- django_bolt/request_parsing.py +128 -0
- django_bolt/responses.py +214 -0
- django_bolt/router.py +48 -0
- django_bolt/serialization.py +193 -0
- django_bolt/status_codes.py +321 -0
- django_bolt/testing/__init__.py +10 -0
- django_bolt/testing/client.py +274 -0
- django_bolt/testing/helpers.py +93 -0
- django_bolt/tests/__init__.py +0 -0
- django_bolt/tests/admin_tests/__init__.py +1 -0
- django_bolt/tests/admin_tests/conftest.py +6 -0
- django_bolt/tests/admin_tests/test_admin_with_django.py +278 -0
- django_bolt/tests/admin_tests/urls.py +9 -0
- django_bolt/tests/cbv/__init__.py +0 -0
- django_bolt/tests/cbv/test_class_views.py +570 -0
- django_bolt/tests/cbv/test_class_views_django_orm.py +703 -0
- django_bolt/tests/cbv/test_class_views_features.py +1173 -0
- django_bolt/tests/cbv/test_class_views_with_client.py +622 -0
- django_bolt/tests/conftest.py +165 -0
- django_bolt/tests/test_action_decorator.py +399 -0
- django_bolt/tests/test_auth_secret_key.py +83 -0
- django_bolt/tests/test_decorator_syntax.py +159 -0
- django_bolt/tests/test_error_handling.py +481 -0
- django_bolt/tests/test_file_response.py +192 -0
- django_bolt/tests/test_global_cors.py +172 -0
- django_bolt/tests/test_guards_auth.py +441 -0
- django_bolt/tests/test_guards_integration.py +303 -0
- django_bolt/tests/test_health.py +283 -0
- django_bolt/tests/test_integration_validation.py +400 -0
- django_bolt/tests/test_json_validation.py +536 -0
- django_bolt/tests/test_jwt_auth.py +327 -0
- django_bolt/tests/test_jwt_token.py +458 -0
- django_bolt/tests/test_logging.py +837 -0
- django_bolt/tests/test_logging_merge.py +419 -0
- django_bolt/tests/test_middleware.py +492 -0
- django_bolt/tests/test_middleware_server.py +230 -0
- django_bolt/tests/test_model_viewset.py +323 -0
- django_bolt/tests/test_models.py +24 -0
- django_bolt/tests/test_pagination.py +1258 -0
- django_bolt/tests/test_parameter_validation.py +178 -0
- django_bolt/tests/test_syntax.py +626 -0
- django_bolt/tests/test_testing_utilities.py +163 -0
- django_bolt/tests/test_testing_utilities_simple.py +123 -0
- django_bolt/tests/test_viewset_unified.py +346 -0
- django_bolt/typing.py +273 -0
- django_bolt/views.py +1110 -0
- django_bolt-0.1.0.dist-info/METADATA +629 -0
- django_bolt-0.1.0.dist-info/RECORD +128 -0
- django_bolt-0.1.0.dist-info/WHEEL +4 -0
- django_bolt-0.1.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Permission/guard system for Django-Bolt.
|
|
3
|
+
|
|
4
|
+
Provides DRF-inspired permission classes (called "guards" in Litestar terminology)
|
|
5
|
+
that are compiled to Rust types for zero-GIL performance.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BasePermission(ABC):
|
|
13
|
+
"""
|
|
14
|
+
Base class for permission guards.
|
|
15
|
+
|
|
16
|
+
Guards are evaluated in Rust after authentication to determine if
|
|
17
|
+
a request should be allowed. This happens before the Python handler
|
|
18
|
+
is called, enabling early 403 responses without GIL overhead.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def guard_name(self) -> str:
|
|
24
|
+
"""Return the guard type name for Rust compilation"""
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def to_metadata(self) -> Dict[str, Any]:
|
|
29
|
+
"""
|
|
30
|
+
Compile this permission guard into metadata for Rust.
|
|
31
|
+
|
|
32
|
+
Returns a dict that will be parsed by Rust into typed enums.
|
|
33
|
+
"""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
def has_permission(self, auth_context: Optional[Any]) -> bool:
|
|
37
|
+
"""
|
|
38
|
+
Check if the authenticated user has permission (Python fallback).
|
|
39
|
+
|
|
40
|
+
This is primarily for documentation/compatibility. The actual check
|
|
41
|
+
happens in Rust for performance.
|
|
42
|
+
"""
|
|
43
|
+
return True
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class AllowAny(BasePermission):
|
|
47
|
+
"""
|
|
48
|
+
Allow any request, authenticated or not.
|
|
49
|
+
|
|
50
|
+
This is the default permission when no guards are specified.
|
|
51
|
+
Using this explicitly bypasses any global default permissions.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def guard_name(self) -> str:
|
|
56
|
+
return "allow_any"
|
|
57
|
+
|
|
58
|
+
def to_metadata(self) -> Dict[str, Any]:
|
|
59
|
+
return {"type": "allow_any"}
|
|
60
|
+
|
|
61
|
+
def has_permission(self, auth_context: Optional[Any]) -> bool:
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class IsAuthenticated(BasePermission):
|
|
66
|
+
"""
|
|
67
|
+
Require that the request is authenticated.
|
|
68
|
+
|
|
69
|
+
Returns 401 if no authentication was successful.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def guard_name(self) -> str:
|
|
74
|
+
return "is_authenticated"
|
|
75
|
+
|
|
76
|
+
def to_metadata(self) -> Dict[str, Any]:
|
|
77
|
+
return {"type": "is_authenticated"}
|
|
78
|
+
|
|
79
|
+
def has_permission(self, auth_context: Optional[Any]) -> bool:
|
|
80
|
+
return auth_context is not None and auth_context.user_id is not None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class IsAdminUser(BasePermission):
|
|
84
|
+
"""
|
|
85
|
+
Require that the authenticated user is an admin/superuser.
|
|
86
|
+
|
|
87
|
+
Returns 403 if user is not admin.
|
|
88
|
+
Requires JWT token to include 'is_superuser' or 'is_admin' claim.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def guard_name(self) -> str:
|
|
93
|
+
return "is_admin"
|
|
94
|
+
|
|
95
|
+
def to_metadata(self) -> Dict[str, Any]:
|
|
96
|
+
return {"type": "is_admin"}
|
|
97
|
+
|
|
98
|
+
def has_permission(self, auth_context: Optional[Any]) -> bool:
|
|
99
|
+
return auth_context is not None and auth_context.is_admin
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class IsStaff(BasePermission):
|
|
103
|
+
"""
|
|
104
|
+
Require that the authenticated user is staff.
|
|
105
|
+
|
|
106
|
+
Returns 403 if user is not staff.
|
|
107
|
+
Requires JWT token to include 'is_staff' claim.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def guard_name(self) -> str:
|
|
112
|
+
return "is_staff"
|
|
113
|
+
|
|
114
|
+
def to_metadata(self) -> Dict[str, Any]:
|
|
115
|
+
return {"type": "is_staff"}
|
|
116
|
+
|
|
117
|
+
def has_permission(self, auth_context: Optional[Any]) -> bool:
|
|
118
|
+
return auth_context is not None and auth_context.is_staff
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class HasPermission(BasePermission):
|
|
122
|
+
"""
|
|
123
|
+
Require that the authenticated user has a specific permission.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
permission: Permission string (e.g., "app.view_model", "api.create_resource")
|
|
127
|
+
|
|
128
|
+
For JWT: token should include "permissions" claim as list of strings
|
|
129
|
+
For API keys: configured via key_permissions mapping in APIKeyAuthentication
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
def __init__(self, permission: str):
|
|
133
|
+
self.permission = permission
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def guard_name(self) -> str:
|
|
137
|
+
return "has_permission"
|
|
138
|
+
|
|
139
|
+
def to_metadata(self) -> Dict[str, Any]:
|
|
140
|
+
return {
|
|
141
|
+
"type": "has_permission",
|
|
142
|
+
"permission": self.permission,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
def has_permission(self, auth_context: Optional[Any]) -> bool:
|
|
146
|
+
if auth_context is None or auth_context.permissions is None:
|
|
147
|
+
return False
|
|
148
|
+
return self.permission in auth_context.permissions
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class HasAnyPermission(BasePermission):
|
|
152
|
+
"""
|
|
153
|
+
Require that the authenticated user has at least one of the specified permissions.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
permissions: List of permission strings
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
def __init__(self, *permissions: str):
|
|
160
|
+
self.permissions = list(permissions)
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def guard_name(self) -> str:
|
|
164
|
+
return "has_any_permission"
|
|
165
|
+
|
|
166
|
+
def to_metadata(self) -> Dict[str, Any]:
|
|
167
|
+
return {
|
|
168
|
+
"type": "has_any_permission",
|
|
169
|
+
"permissions": self.permissions,
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
def has_permission(self, auth_context: Optional[Any]) -> bool:
|
|
173
|
+
if auth_context is None or auth_context.permissions is None:
|
|
174
|
+
return False
|
|
175
|
+
return any(perm in auth_context.permissions for perm in self.permissions)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class HasAllPermissions(BasePermission):
|
|
179
|
+
"""
|
|
180
|
+
Require that the authenticated user has all of the specified permissions.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
permissions: List of permission strings
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
def __init__(self, *permissions: str):
|
|
187
|
+
self.permissions = list(permissions)
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def guard_name(self) -> str:
|
|
191
|
+
return "has_all_permissions"
|
|
192
|
+
|
|
193
|
+
def to_metadata(self) -> Dict[str, Any]:
|
|
194
|
+
return {
|
|
195
|
+
"type": "has_all_permissions",
|
|
196
|
+
"permissions": self.permissions,
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
def has_permission(self, auth_context: Optional[Any]) -> bool:
|
|
200
|
+
if auth_context is None or auth_context.permissions is None:
|
|
201
|
+
return False
|
|
202
|
+
return all(perm in auth_context.permissions for perm in self.permissions)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def get_default_permission_classes() -> List[BasePermission]:
|
|
206
|
+
"""
|
|
207
|
+
Get default permission classes from Django settings.
|
|
208
|
+
|
|
209
|
+
Looks for BOLT_DEFAULT_PERMISSION_CLASSES in settings. If not found,
|
|
210
|
+
returns [AllowAny()] (no restrictions by default).
|
|
211
|
+
"""
|
|
212
|
+
try:
|
|
213
|
+
from django.conf import settings
|
|
214
|
+
from django.core.exceptions import ImproperlyConfigured
|
|
215
|
+
try:
|
|
216
|
+
if hasattr(settings, 'BOLT_DEFAULT_PERMISSION_CLASSES'):
|
|
217
|
+
return settings.BOLT_DEFAULT_PERMISSION_CLASSES
|
|
218
|
+
except ImproperlyConfigured:
|
|
219
|
+
# Settings not configured, return default
|
|
220
|
+
pass
|
|
221
|
+
except (ImportError, AttributeError):
|
|
222
|
+
pass
|
|
223
|
+
|
|
224
|
+
return [AllowAny()]
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""
|
|
2
|
+
JWT utility functions for Django-Bolt.
|
|
3
|
+
|
|
4
|
+
Provides helper functions to create JWT tokens for Django users and
|
|
5
|
+
extract user information from request context.
|
|
6
|
+
"""
|
|
7
|
+
import time
|
|
8
|
+
import jwt
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
|
+
from django.contrib.auth import get_user_model
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def create_jwt_for_user(
|
|
14
|
+
user,
|
|
15
|
+
secret: Optional[str] = None,
|
|
16
|
+
algorithm: str = "HS256",
|
|
17
|
+
expires_in: int = 3600,
|
|
18
|
+
extra_claims: Optional[Dict[str, Any]] = None
|
|
19
|
+
) -> str:
|
|
20
|
+
"""
|
|
21
|
+
Create a JWT token for a Django User.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
user: Django User model instance
|
|
25
|
+
secret: JWT secret key. If None, uses Django's SECRET_KEY
|
|
26
|
+
algorithm: JWT algorithm (default: "HS256")
|
|
27
|
+
expires_in: Token expiration time in seconds (default: 3600 = 1 hour)
|
|
28
|
+
extra_claims: Additional claims to include in the token
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
JWT token string
|
|
32
|
+
|
|
33
|
+
Standard claims included:
|
|
34
|
+
- sub: user.id (subject - user primary key)
|
|
35
|
+
- exp: expiration time (current time + expires_in)
|
|
36
|
+
- iat: issued at time (current timestamp)
|
|
37
|
+
- is_staff: user.is_staff
|
|
38
|
+
- is_superuser: user.is_superuser
|
|
39
|
+
- username: user.username (for reference)
|
|
40
|
+
- email: user.email (if available)
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
```python
|
|
44
|
+
from django.contrib.auth import get_user_model
|
|
45
|
+
from django_bolt.jwt_utils import create_jwt_for_user
|
|
46
|
+
|
|
47
|
+
User = get_user_model()
|
|
48
|
+
user = await User.objects.aget(username="john")
|
|
49
|
+
|
|
50
|
+
# Create a basic token
|
|
51
|
+
token = create_jwt_for_user(user)
|
|
52
|
+
|
|
53
|
+
# Create token with custom expiration and extra claims
|
|
54
|
+
token = create_jwt_for_user(
|
|
55
|
+
user,
|
|
56
|
+
expires_in=7200, # 2 hours
|
|
57
|
+
extra_claims={
|
|
58
|
+
"permissions": ["read", "write"],
|
|
59
|
+
"role": "admin",
|
|
60
|
+
"tenant_id": "acme-corp"
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
```
|
|
64
|
+
"""
|
|
65
|
+
# Use Django SECRET_KEY if no secret provided
|
|
66
|
+
if secret is None:
|
|
67
|
+
from django.conf import settings
|
|
68
|
+
secret = settings.SECRET_KEY
|
|
69
|
+
|
|
70
|
+
# Build standard claims
|
|
71
|
+
now = int(time.time())
|
|
72
|
+
payload = {
|
|
73
|
+
"sub": str(user.id), # Subject: user ID as string
|
|
74
|
+
"exp": now + expires_in, # Expiration time
|
|
75
|
+
"iat": now, # Issued at
|
|
76
|
+
"is_staff": user.is_staff,
|
|
77
|
+
"is_superuser": user.is_superuser,
|
|
78
|
+
"username": user.username,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Add email if available
|
|
82
|
+
if hasattr(user, 'email') and user.email:
|
|
83
|
+
payload["email"] = user.email
|
|
84
|
+
|
|
85
|
+
# Add first/last name if available
|
|
86
|
+
if hasattr(user, 'first_name') and user.first_name:
|
|
87
|
+
payload["first_name"] = user.first_name
|
|
88
|
+
if hasattr(user, 'last_name') and user.last_name:
|
|
89
|
+
payload["last_name"] = user.last_name
|
|
90
|
+
|
|
91
|
+
# Merge extra claims
|
|
92
|
+
if extra_claims:
|
|
93
|
+
payload.update(extra_claims)
|
|
94
|
+
|
|
95
|
+
return jwt.encode(payload, secret, algorithm=algorithm)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
async def get_current_user(request: Dict[str, Any]):
|
|
99
|
+
"""
|
|
100
|
+
Dependency function to extract and fetch Django User from request context.
|
|
101
|
+
|
|
102
|
+
This is a reusable dependency that can be used with Depends() to inject
|
|
103
|
+
the current authenticated Django User into your handlers.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
request: Request dictionary with context
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Django User instance or None if not authenticated or not found
|
|
110
|
+
|
|
111
|
+
Example:
|
|
112
|
+
```python
|
|
113
|
+
from django_bolt import BoltAPI
|
|
114
|
+
from django_bolt.auth import JWTAuthentication
|
|
115
|
+
from django_bolt.permissions import IsAuthenticated
|
|
116
|
+
from django_bolt.params import Depends
|
|
117
|
+
from django_bolt.jwt_utils import get_current_user
|
|
118
|
+
|
|
119
|
+
api = BoltAPI()
|
|
120
|
+
|
|
121
|
+
@api.get(
|
|
122
|
+
"/me",
|
|
123
|
+
auth=[JWTAuthentication()],
|
|
124
|
+
guards=[IsAuthenticated()]
|
|
125
|
+
)
|
|
126
|
+
async def get_my_profile(user=Depends(get_current_user)):
|
|
127
|
+
return {
|
|
128
|
+
"id": user.id,
|
|
129
|
+
"username": user.username,
|
|
130
|
+
"email": user.email,
|
|
131
|
+
"is_staff": user.is_staff,
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
"""
|
|
135
|
+
User = get_user_model()
|
|
136
|
+
context = request.get("context", {})
|
|
137
|
+
user_id = context.get("user_id")
|
|
138
|
+
|
|
139
|
+
if not user_id:
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
# Fetch User from database using async ORM
|
|
144
|
+
user = await User.objects.aget(pk=user_id)
|
|
145
|
+
return user
|
|
146
|
+
except (User.DoesNotExist, ValueError, TypeError):
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def extract_user_id_from_context(request: Dict[str, Any]) -> Optional[str]:
|
|
151
|
+
"""
|
|
152
|
+
Extract user_id from request context.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
request: Request dictionary with context
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
User ID as string or None if not present
|
|
159
|
+
|
|
160
|
+
Example:
|
|
161
|
+
```python
|
|
162
|
+
@api.get("/data")
|
|
163
|
+
async def get_data(request: dict):
|
|
164
|
+
user_id = extract_user_id_from_context(request)
|
|
165
|
+
if user_id:
|
|
166
|
+
# Use user_id for filtering, logging, etc.
|
|
167
|
+
data = await MyModel.objects.filter(user_id=user_id).all()
|
|
168
|
+
return {"data": data}
|
|
169
|
+
return {"error": "Not authenticated"}
|
|
170
|
+
```
|
|
171
|
+
"""
|
|
172
|
+
context = request.get("context", {})
|
|
173
|
+
return context.get("user_id")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def get_auth_context(request: Dict[str, Any]) -> Dict[str, Any]:
|
|
177
|
+
"""
|
|
178
|
+
Get the full authentication context from request.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
request: Request dictionary with context
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Authentication context dictionary containing:
|
|
185
|
+
- user_id: User identifier
|
|
186
|
+
- is_staff: Staff status boolean
|
|
187
|
+
- is_admin: Admin/superuser status boolean
|
|
188
|
+
- auth_backend: Authentication backend used (jwt, api_key, etc.)
|
|
189
|
+
- permissions: List of permissions (if available)
|
|
190
|
+
- auth_claims: JWT claims dict (if JWT auth was used)
|
|
191
|
+
|
|
192
|
+
Example:
|
|
193
|
+
```python
|
|
194
|
+
@api.get("/admin/stats")
|
|
195
|
+
async def admin_stats(request: dict):
|
|
196
|
+
auth_ctx = get_auth_context(request)
|
|
197
|
+
|
|
198
|
+
if not auth_ctx.get("is_admin"):
|
|
199
|
+
return {"error": "Admin access required"}
|
|
200
|
+
|
|
201
|
+
# Use user_id for audit logging
|
|
202
|
+
user_id = auth_ctx["user_id"]
|
|
203
|
+
backend = auth_ctx["auth_backend"]
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
"authenticated_as": user_id,
|
|
207
|
+
"via": backend,
|
|
208
|
+
"stats": {...}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
"""
|
|
212
|
+
return request.get("context", {})
|