mdb-engine 0.2.1__py3-none-any.whl → 0.2.3__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.
Files changed (65) hide show
  1. mdb_engine/auth/audit.py +40 -40
  2. mdb_engine/auth/base.py +3 -3
  3. mdb_engine/auth/casbin_factory.py +6 -6
  4. mdb_engine/auth/config_defaults.py +5 -5
  5. mdb_engine/auth/config_helpers.py +12 -12
  6. mdb_engine/auth/cookie_utils.py +9 -9
  7. mdb_engine/auth/csrf.py +9 -8
  8. mdb_engine/auth/decorators.py +7 -6
  9. mdb_engine/auth/dependencies.py +22 -21
  10. mdb_engine/auth/integration.py +9 -9
  11. mdb_engine/auth/jwt.py +9 -9
  12. mdb_engine/auth/middleware.py +4 -3
  13. mdb_engine/auth/oso_factory.py +6 -6
  14. mdb_engine/auth/provider.py +4 -4
  15. mdb_engine/auth/rate_limiter.py +12 -11
  16. mdb_engine/auth/restrictions.py +16 -15
  17. mdb_engine/auth/session_manager.py +11 -13
  18. mdb_engine/auth/shared_middleware.py +16 -15
  19. mdb_engine/auth/shared_users.py +20 -20
  20. mdb_engine/auth/token_lifecycle.py +10 -12
  21. mdb_engine/auth/token_store.py +4 -5
  22. mdb_engine/auth/users.py +51 -52
  23. mdb_engine/auth/utils.py +29 -33
  24. mdb_engine/cli/commands/generate.py +6 -6
  25. mdb_engine/cli/utils.py +4 -4
  26. mdb_engine/config.py +6 -7
  27. mdb_engine/core/app_registration.py +12 -12
  28. mdb_engine/core/app_secrets.py +1 -2
  29. mdb_engine/core/connection.py +3 -4
  30. mdb_engine/core/encryption.py +1 -2
  31. mdb_engine/core/engine.py +43 -44
  32. mdb_engine/core/manifest.py +59 -58
  33. mdb_engine/core/ray_integration.py +10 -9
  34. mdb_engine/core/seeding.py +3 -3
  35. mdb_engine/core/service_initialization.py +10 -9
  36. mdb_engine/core/types.py +40 -40
  37. mdb_engine/database/abstraction.py +15 -16
  38. mdb_engine/database/connection.py +40 -12
  39. mdb_engine/database/query_validator.py +8 -8
  40. mdb_engine/database/resource_limiter.py +7 -7
  41. mdb_engine/database/scoped_wrapper.py +51 -58
  42. mdb_engine/dependencies.py +14 -13
  43. mdb_engine/di/container.py +12 -13
  44. mdb_engine/di/providers.py +14 -13
  45. mdb_engine/di/scopes.py +5 -5
  46. mdb_engine/embeddings/dependencies.py +2 -2
  47. mdb_engine/embeddings/service.py +31 -43
  48. mdb_engine/exceptions.py +20 -20
  49. mdb_engine/indexes/helpers.py +11 -11
  50. mdb_engine/indexes/manager.py +9 -9
  51. mdb_engine/memory/service.py +30 -30
  52. mdb_engine/observability/health.py +10 -9
  53. mdb_engine/observability/logging.py +10 -10
  54. mdb_engine/observability/metrics.py +8 -7
  55. mdb_engine/repositories/base.py +25 -25
  56. mdb_engine/repositories/mongo.py +17 -17
  57. mdb_engine/repositories/unit_of_work.py +6 -6
  58. mdb_engine/routing/websockets.py +19 -18
  59. {mdb_engine-0.2.1.dist-info → mdb_engine-0.2.3.dist-info}/METADATA +8 -8
  60. mdb_engine-0.2.3.dist-info/RECORD +96 -0
  61. mdb_engine-0.2.1.dist-info/RECORD +0 -96
  62. {mdb_engine-0.2.1.dist-info → mdb_engine-0.2.3.dist-info}/WHEEL +0 -0
  63. {mdb_engine-0.2.1.dist-info → mdb_engine-0.2.3.dist-info}/entry_points.txt +0 -0
  64. {mdb_engine-0.2.1.dist-info → mdb_engine-0.2.3.dist-info}/licenses/LICENSE +0 -0
  65. {mdb_engine-0.2.1.dist-info → mdb_engine-0.2.3.dist-info}/top_level.txt +0 -0
mdb_engine/auth/audit.py CHANGED
@@ -35,7 +35,7 @@ Usage:
35
35
  import logging
36
36
  from datetime import datetime, timedelta
37
37
  from enum import Enum
38
- from typing import Any, Dict, List, Optional
38
+ from typing import Any
39
39
 
40
40
  from motor.motor_asyncio import AsyncIOMotorDatabase
41
41
  from pymongo.errors import OperationFailure
@@ -161,12 +161,12 @@ class AuthAuditLog:
161
161
  self,
162
162
  action: AuthAction,
163
163
  success: bool,
164
- user_email: Optional[str] = None,
165
- user_id: Optional[str] = None,
166
- app_slug: Optional[str] = None,
167
- ip_address: Optional[str] = None,
168
- user_agent: Optional[str] = None,
169
- details: Optional[Dict[str, Any]] = None,
164
+ user_email: str | None = None,
165
+ user_id: str | None = None,
166
+ app_slug: str | None = None,
167
+ ip_address: str | None = None,
168
+ user_agent: str | None = None,
169
+ details: dict[str, Any] | None = None,
170
170
  ) -> str:
171
171
  """
172
172
  Log an authentication event.
@@ -217,9 +217,9 @@ class AuthAuditLog:
217
217
  async def log_login_success(
218
218
  self,
219
219
  email: str,
220
- ip_address: Optional[str] = None,
221
- user_agent: Optional[str] = None,
222
- app_slug: Optional[str] = None,
220
+ ip_address: str | None = None,
221
+ user_agent: str | None = None,
222
+ app_slug: str | None = None,
223
223
  ) -> str:
224
224
  """Convenience method to log successful login."""
225
225
  return await self.log_event(
@@ -235,9 +235,9 @@ class AuthAuditLog:
235
235
  self,
236
236
  email: str,
237
237
  reason: str = "invalid_credentials",
238
- ip_address: Optional[str] = None,
239
- user_agent: Optional[str] = None,
240
- app_slug: Optional[str] = None,
238
+ ip_address: str | None = None,
239
+ user_agent: str | None = None,
240
+ app_slug: str | None = None,
241
241
  ) -> str:
242
242
  """Convenience method to log failed login."""
243
243
  return await self.log_event(
@@ -253,8 +253,8 @@ class AuthAuditLog:
253
253
  async def log_logout(
254
254
  self,
255
255
  email: str,
256
- ip_address: Optional[str] = None,
257
- app_slug: Optional[str] = None,
256
+ ip_address: str | None = None,
257
+ app_slug: str | None = None,
258
258
  ) -> str:
259
259
  """Convenience method to log logout."""
260
260
  return await self.log_event(
@@ -268,9 +268,9 @@ class AuthAuditLog:
268
268
  async def log_register(
269
269
  self,
270
270
  email: str,
271
- ip_address: Optional[str] = None,
272
- user_agent: Optional[str] = None,
273
- app_slug: Optional[str] = None,
271
+ ip_address: str | None = None,
272
+ user_agent: str | None = None,
273
+ app_slug: str | None = None,
274
274
  ) -> str:
275
275
  """Convenience method to log new user registration."""
276
276
  return await self.log_event(
@@ -286,10 +286,10 @@ class AuthAuditLog:
286
286
  self,
287
287
  email: str,
288
288
  app_slug: str,
289
- old_roles: List[str],
290
- new_roles: List[str],
291
- changed_by: Optional[str] = None,
292
- ip_address: Optional[str] = None,
289
+ old_roles: list[str],
290
+ new_roles: list[str],
291
+ changed_by: str | None = None,
292
+ ip_address: str | None = None,
293
293
  ) -> str:
294
294
  """Log a role change event."""
295
295
  action = (
@@ -312,8 +312,8 @@ class AuthAuditLog:
312
312
  self,
313
313
  email: str,
314
314
  reason: str = "logout",
315
- ip_address: Optional[str] = None,
316
- app_slug: Optional[str] = None,
315
+ ip_address: str | None = None,
316
+ app_slug: str | None = None,
317
317
  ) -> str:
318
318
  """Log token revocation."""
319
319
  return await self.log_event(
@@ -329,8 +329,8 @@ class AuthAuditLog:
329
329
  self,
330
330
  ip_address: str,
331
331
  endpoint: str,
332
- email: Optional[str] = None,
333
- app_slug: Optional[str] = None,
332
+ email: str | None = None,
333
+ app_slug: str | None = None,
334
334
  ) -> str:
335
335
  """Log rate limit exceeded event."""
336
336
  return await self.log_event(
@@ -349,10 +349,10 @@ class AuthAuditLog:
349
349
  async def get_recent_events(
350
350
  self,
351
351
  hours: int = 24,
352
- action: Optional[AuthAction] = None,
353
- success: Optional[bool] = None,
352
+ action: AuthAction | None = None,
353
+ success: bool | None = None,
354
354
  limit: int = 100,
355
- ) -> List[Dict[str, Any]]:
355
+ ) -> list[dict[str, Any]]:
356
356
  """
357
357
  Get recent audit events.
358
358
 
@@ -367,7 +367,7 @@ class AuthAuditLog:
367
367
  """
368
368
  since = datetime.utcnow() - timedelta(hours=hours)
369
369
 
370
- query: Dict[str, Any] = {"timestamp": {"$gte": since}}
370
+ query: dict[str, Any] = {"timestamp": {"$gte": since}}
371
371
  if action:
372
372
  query["action"] = action.value if isinstance(action, AuthAction) else action
373
373
  if success is not None:
@@ -384,11 +384,11 @@ class AuthAuditLog:
384
384
 
385
385
  async def get_failed_logins(
386
386
  self,
387
- email: Optional[str] = None,
388
- ip_address: Optional[str] = None,
387
+ email: str | None = None,
388
+ ip_address: str | None = None,
389
389
  hours: int = 24,
390
390
  limit: int = 100,
391
- ) -> List[Dict[str, Any]]:
391
+ ) -> list[dict[str, Any]]:
392
392
  """
393
393
  Get failed login attempts.
394
394
 
@@ -403,7 +403,7 @@ class AuthAuditLog:
403
403
  """
404
404
  since = datetime.utcnow() - timedelta(hours=hours)
405
405
 
406
- query: Dict[str, Any] = {
406
+ query: dict[str, Any] = {
407
407
  "action": AuthAction.LOGIN_FAILED.value,
408
408
  "timestamp": {"$gte": since},
409
409
  }
@@ -425,7 +425,7 @@ class AuthAuditLog:
425
425
  email: str,
426
426
  hours: int = 168, # 7 days
427
427
  limit: int = 100,
428
- ) -> List[Dict[str, Any]]:
428
+ ) -> list[dict[str, Any]]:
429
429
  """
430
430
  Get all activity for a specific user.
431
431
 
@@ -462,7 +462,7 @@ class AuthAuditLog:
462
462
  ip_address: str,
463
463
  hours: int = 24,
464
464
  limit: int = 100,
465
- ) -> List[Dict[str, Any]]:
465
+ ) -> list[dict[str, Any]]:
466
466
  """
467
467
  Get all activity from a specific IP address.
468
468
 
@@ -498,8 +498,8 @@ class AuthAuditLog:
498
498
 
499
499
  async def count_failed_logins(
500
500
  self,
501
- email: Optional[str] = None,
502
- ip_address: Optional[str] = None,
501
+ email: str | None = None,
502
+ ip_address: str | None = None,
503
503
  hours: int = 1,
504
504
  ) -> int:
505
505
  """
@@ -517,7 +517,7 @@ class AuthAuditLog:
517
517
  """
518
518
  since = datetime.utcnow() - timedelta(hours=hours)
519
519
 
520
- query: Dict[str, Any] = {
520
+ query: dict[str, Any] = {
521
521
  "action": AuthAction.LOGIN_FAILED.value,
522
522
  "timestamp": {"$gte": since},
523
523
  }
@@ -531,7 +531,7 @@ class AuthAuditLog:
531
531
  async def get_security_summary(
532
532
  self,
533
533
  hours: int = 24,
534
- ) -> Dict[str, Any]:
534
+ ) -> dict[str, Any]:
535
535
  """
536
536
  Get security summary statistics.
537
537
 
mdb_engine/auth/base.py CHANGED
@@ -11,7 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  import abc
13
13
  import logging
14
- from typing import Any, Optional
14
+ from typing import Any
15
15
 
16
16
  logger = logging.getLogger(__name__)
17
17
 
@@ -71,7 +71,7 @@ class BaseAuthorizationProvider(abc.ABC):
71
71
  subject: str,
72
72
  resource: str,
73
73
  action: str,
74
- user_object: Optional[dict[str, Any]] = None,
74
+ user_object: dict[str, Any] | None = None,
75
75
  ) -> bool:
76
76
  """
77
77
  Check if a subject is allowed to perform an action on a resource.
@@ -195,7 +195,7 @@ class BaseAuthorizationProvider(abc.ABC):
195
195
  resource: str,
196
196
  action: str,
197
197
  error: Exception,
198
- context: Optional[str] = None,
198
+ context: str | None = None,
199
199
  ) -> bool:
200
200
  """
201
201
  Handle authorization evaluation errors with fail-closed security.
@@ -11,7 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  import logging
13
13
  from pathlib import Path
14
- from typing import TYPE_CHECKING, Any, Optional
14
+ from typing import TYPE_CHECKING, Any
15
15
 
16
16
  from .casbin_models import DEFAULT_RBAC_MODEL, SIMPLE_ACL_MODEL
17
17
 
@@ -46,7 +46,7 @@ async def get_casbin_model(model_type: str = "rbac") -> str:
46
46
  # Try async file reading (non-blocking)
47
47
  import aiofiles
48
48
 
49
- async with aiofiles.open(model_path, "r") as f:
49
+ async with aiofiles.open(model_path) as f:
50
50
  content = await f.read()
51
51
  logger.debug(f"Read model file asynchronously: {model_path}")
52
52
  return content
@@ -67,7 +67,7 @@ async def create_casbin_enforcer(
67
67
  db_name: str,
68
68
  model: str = "rbac",
69
69
  policies_collection: str = "casbin_policies",
70
- default_roles: Optional[list] = None,
70
+ default_roles: list | None = None,
71
71
  ) -> casbin.AsyncEnforcer:
72
72
  """
73
73
  Create a Casbin AsyncEnforcer with MongoDB adapter.
@@ -189,7 +189,7 @@ async def create_casbin_enforcer(
189
189
 
190
190
  async def initialize_casbin_from_manifest(
191
191
  engine, app_slug: str, auth_config: dict[str, Any]
192
- ) -> Optional[CasbinAdapter]:
192
+ ) -> CasbinAdapter | None:
193
193
  """
194
194
  Initialize Casbin provider from manifest configuration.
195
195
 
@@ -261,7 +261,7 @@ async def initialize_casbin_from_manifest(
261
261
  if initial_policies:
262
262
  logger.info(f"Setting up {len(initial_policies)} initial policies...")
263
263
  for policy in initial_policies:
264
- if isinstance(policy, (list, tuple)) and len(policy) >= 3:
264
+ if isinstance(policy, list | tuple) and len(policy) >= 3:
265
265
  role, resource, action = policy[0], policy[1], policy[2]
266
266
  try:
267
267
  # Check if policy already exists
@@ -331,7 +331,7 @@ async def initialize_casbin_from_manifest(
331
331
  if initial_policies:
332
332
  verified = 0
333
333
  for policy in initial_policies:
334
- if isinstance(policy, (list, tuple)) and len(policy) >= 3:
334
+ if isinstance(policy, list | tuple) and len(policy) >= 3:
335
335
  role, resource, action = policy[0], policy[1], policy[2]
336
336
  if await adapter.has_policy(role, resource, action):
337
337
  verified += 1
@@ -7,9 +7,9 @@ This module provides a single source of truth for all config defaults.
7
7
  This module is part of MDB_ENGINE - MongoDB Engine.
8
8
  """
9
9
 
10
- from typing import Any, Dict
10
+ from typing import Any
11
11
 
12
- SECURITY_CONFIG_DEFAULTS: Dict[str, Any] = {
12
+ SECURITY_CONFIG_DEFAULTS: dict[str, Any] = {
13
13
  "password_policy": {
14
14
  "allow_plain_text": False,
15
15
  "min_length": 8,
@@ -35,7 +35,7 @@ SECURITY_CONFIG_DEFAULTS: Dict[str, Any] = {
35
35
  "token_fingerprinting": {"enabled": True, "bind_to_device": True},
36
36
  }
37
37
 
38
- TOKEN_MANAGEMENT_DEFAULTS: Dict[str, Any] = {
38
+ TOKEN_MANAGEMENT_DEFAULTS: dict[str, Any] = {
39
39
  "enabled": True,
40
40
  "access_token_ttl": 900,
41
41
  "refresh_token_ttl": 604800,
@@ -45,7 +45,7 @@ TOKEN_MANAGEMENT_DEFAULTS: Dict[str, Any] = {
45
45
  "auto_setup": True,
46
46
  }
47
47
 
48
- CORS_DEFAULTS: Dict[str, Any] = {
48
+ CORS_DEFAULTS: dict[str, Any] = {
49
49
  "enabled": False,
50
50
  "allow_origins": ["*"],
51
51
  "allow_credentials": False,
@@ -54,7 +54,7 @@ CORS_DEFAULTS: Dict[str, Any] = {
54
54
  "max_age": 3600,
55
55
  }
56
56
 
57
- OBSERVABILITY_DEFAULTS: Dict[str, Any] = {
57
+ OBSERVABILITY_DEFAULTS: dict[str, Any] = {
58
58
  "health_checks": {"enabled": True, "endpoint": "/health", "interval_seconds": 30},
59
59
  "metrics": {
60
60
  "enabled": True,
@@ -8,7 +8,7 @@ This module is part of MDB_ENGINE - MongoDB Engine.
8
8
  """
9
9
 
10
10
  import logging
11
- from typing import Any, Dict
11
+ from typing import Any
12
12
 
13
13
  from fastapi import Request
14
14
 
@@ -23,8 +23,8 @@ logger = logging.getLogger(__name__)
23
23
 
24
24
 
25
25
  def merge_config_with_defaults(
26
- user_config: Dict[str, Any], defaults: Dict[str, Any]
27
- ) -> Dict[str, Any]:
26
+ user_config: dict[str, Any], defaults: dict[str, Any]
27
+ ) -> dict[str, Any]:
28
28
  """
29
29
  Deep merge user config with defaults.
30
30
 
@@ -55,7 +55,7 @@ def merge_config_with_defaults(
55
55
  return merged
56
56
 
57
57
 
58
- def get_security_config(request: Request) -> Dict[str, Any]:
58
+ def get_security_config(request: Request) -> dict[str, Any]:
59
59
  """
60
60
  Get security configuration from app.state with defaults merged.
61
61
 
@@ -75,7 +75,7 @@ def get_security_config(request: Request) -> Dict[str, Any]:
75
75
  return SECURITY_CONFIG_DEFAULTS.copy()
76
76
 
77
77
 
78
- def get_password_policy(request: Request) -> Dict[str, Any]:
78
+ def get_password_policy(request: Request) -> dict[str, Any]:
79
79
  """
80
80
  Get password policy configuration with defaults merged.
81
81
 
@@ -91,7 +91,7 @@ def get_password_policy(request: Request) -> Dict[str, Any]:
91
91
  )
92
92
 
93
93
 
94
- def get_session_fingerprinting_config(request: Request) -> Dict[str, Any]:
94
+ def get_session_fingerprinting_config(request: Request) -> dict[str, Any]:
95
95
  """
96
96
  Get session fingerprinting configuration with defaults merged.
97
97
 
@@ -108,7 +108,7 @@ def get_session_fingerprinting_config(request: Request) -> Dict[str, Any]:
108
108
  )
109
109
 
110
110
 
111
- def get_account_lockout_config(request: Request) -> Dict[str, Any]:
111
+ def get_account_lockout_config(request: Request) -> dict[str, Any]:
112
112
  """
113
113
  Get account lockout configuration with defaults merged.
114
114
 
@@ -124,7 +124,7 @@ def get_account_lockout_config(request: Request) -> Dict[str, Any]:
124
124
  )
125
125
 
126
126
 
127
- def get_ip_validation_config(request: Request) -> Dict[str, Any]:
127
+ def get_ip_validation_config(request: Request) -> dict[str, Any]:
128
128
  """
129
129
  Get IP validation configuration with defaults merged.
130
130
 
@@ -138,7 +138,7 @@ def get_ip_validation_config(request: Request) -> Dict[str, Any]:
138
138
  return security_config.get("ip_validation", SECURITY_CONFIG_DEFAULTS["ip_validation"].copy())
139
139
 
140
140
 
141
- def get_token_fingerprinting_config(request: Request) -> Dict[str, Any]:
141
+ def get_token_fingerprinting_config(request: Request) -> dict[str, Any]:
142
142
  """
143
143
  Get token fingerprinting configuration with defaults merged.
144
144
 
@@ -154,7 +154,7 @@ def get_token_fingerprinting_config(request: Request) -> Dict[str, Any]:
154
154
  )
155
155
 
156
156
 
157
- def get_token_management_config(request: Request) -> Dict[str, Any]:
157
+ def get_token_management_config(request: Request) -> dict[str, Any]:
158
158
  """
159
159
  Get token management configuration from app.state with defaults merged.
160
160
 
@@ -174,7 +174,7 @@ def get_token_management_config(request: Request) -> Dict[str, Any]:
174
174
  return TOKEN_MANAGEMENT_DEFAULTS.copy()
175
175
 
176
176
 
177
- def get_cors_config(request: Request) -> Dict[str, Any]:
177
+ def get_cors_config(request: Request) -> dict[str, Any]:
178
178
  """
179
179
  Get CORS configuration from app.state with defaults merged.
180
180
 
@@ -194,7 +194,7 @@ def get_cors_config(request: Request) -> Dict[str, Any]:
194
194
  return CORS_DEFAULTS.copy()
195
195
 
196
196
 
197
- def get_observability_config(request: Request) -> Dict[str, Any]:
197
+ def get_observability_config(request: Request) -> dict[str, Any]:
198
198
  """
199
199
  Get observability configuration from app.state with defaults merged.
200
200
 
@@ -8,7 +8,7 @@ This module is part of MDB_ENGINE - MongoDB Engine.
8
8
 
9
9
  import logging
10
10
  import os
11
- from typing import Any, Dict, Optional
11
+ from typing import Any
12
12
 
13
13
  from fastapi import Request
14
14
 
@@ -16,8 +16,8 @@ logger = logging.getLogger(__name__)
16
16
 
17
17
 
18
18
  def get_secure_cookie_settings(
19
- request: Request, config: Optional[Dict[str, Any]] = None
20
- ) -> Dict[str, Any]:
19
+ request: Request, config: dict[str, Any] | None = None
20
+ ) -> dict[str, Any]:
21
21
  """
22
22
  Get secure cookie settings based on manifest config and request environment.
23
23
 
@@ -76,11 +76,11 @@ def get_secure_cookie_settings(
76
76
  def set_auth_cookies(
77
77
  response,
78
78
  access_token: str,
79
- refresh_token: Optional[str] = None,
80
- request: Optional[Request] = None,
81
- config: Optional[Dict[str, Any]] = None,
82
- access_token_ttl: Optional[int] = None,
83
- refresh_token_ttl: Optional[int] = None,
79
+ refresh_token: str | None = None,
80
+ request: Request | None = None,
81
+ config: dict[str, Any] | None = None,
82
+ access_token_ttl: int | None = None,
83
+ refresh_token_ttl: int | None = None,
84
84
  ):
85
85
  """
86
86
  Set authentication cookies on a response with secure settings.
@@ -130,7 +130,7 @@ def set_auth_cookies(
130
130
  )
131
131
 
132
132
 
133
- def clear_auth_cookies(response, request: Optional[Request] = None):
133
+ def clear_auth_cookies(response, request: Request | None = None):
134
134
  """
135
135
  Clear authentication cookies from response.
136
136
 
mdb_engine/auth/csrf.py CHANGED
@@ -36,7 +36,8 @@ import logging
36
36
  import os
37
37
  import secrets
38
38
  import time
39
- from typing import Any, Awaitable, Callable, Dict, List, Optional, Set
39
+ from collections.abc import Awaitable, Callable
40
+ from typing import Any
40
41
 
41
42
  from fastapi import Request, Response, status
42
43
  from fastapi.responses import JSONResponse
@@ -55,7 +56,7 @@ DEFAULT_TOKEN_TTL = 3600 # 1 hour
55
56
  UNSAFE_METHODS = {"POST", "PUT", "DELETE", "PATCH"}
56
57
 
57
58
 
58
- def generate_csrf_token(secret: Optional[str] = None) -> str:
59
+ def generate_csrf_token(secret: str | None = None) -> str:
59
60
  """
60
61
  Generate a cryptographically secure CSRF token.
61
62
 
@@ -79,7 +80,7 @@ def generate_csrf_token(secret: Optional[str] = None) -> str:
79
80
 
80
81
  def validate_csrf_token(
81
82
  token: str,
82
- secret: Optional[str] = None,
83
+ secret: str | None = None,
83
84
  max_age: int = DEFAULT_TOKEN_TTL,
84
85
  ) -> bool:
85
86
  """
@@ -146,9 +147,9 @@ class CSRFMiddleware(BaseHTTPMiddleware):
146
147
  def __init__(
147
148
  self,
148
149
  app,
149
- secret: Optional[str] = None,
150
- exempt_routes: Optional[List[str]] = None,
151
- exempt_methods: Optional[Set[str]] = None,
150
+ secret: str | None = None,
151
+ exempt_routes: list[str] | None = None,
152
+ exempt_methods: set[str] | None = None,
152
153
  cookie_name: str = CSRF_COOKIE_NAME,
153
154
  header_name: str = CSRF_HEADER_NAME,
154
155
  form_field: str = CSRF_FORM_FIELD,
@@ -300,8 +301,8 @@ class CSRFMiddleware(BaseHTTPMiddleware):
300
301
 
301
302
 
302
303
  def create_csrf_middleware(
303
- manifest_auth: Dict[str, Any],
304
- secret: Optional[str] = None,
304
+ manifest_auth: dict[str, Any],
305
+ secret: str | None = None,
305
306
  ) -> type:
306
307
  """
307
308
  Create CSRF middleware from manifest configuration.
@@ -9,8 +9,9 @@ This module is part of MDB_ENGINE - MongoDB Engine.
9
9
  import logging
10
10
  import time
11
11
  from collections import defaultdict
12
+ from collections.abc import Awaitable, Callable
12
13
  from functools import wraps
13
- from typing import Any, Awaitable, Callable, Dict, Optional
14
+ from typing import Any
14
15
 
15
16
  from fastapi import HTTPException, Request, status
16
17
  from fastapi.responses import RedirectResponse
@@ -20,7 +21,7 @@ from .dependencies import get_current_user_from_request
20
21
  logger = logging.getLogger(__name__)
21
22
 
22
23
  # Rate limiting storage (in-memory, can be replaced with Redis for distributed systems)
23
- _rate_limit_storage: Dict[str, Dict[str, Any]] = defaultdict(dict)
24
+ _rate_limit_storage: dict[str, dict[str, Any]] = defaultdict(dict)
24
25
 
25
26
 
26
27
  def require_auth(redirect_to: str = "/login"):
@@ -82,7 +83,7 @@ def _validate_https(request: Request) -> None:
82
83
  )
83
84
 
84
85
 
85
- async def _get_csrf_token(request: Request) -> Optional[str]:
86
+ async def _get_csrf_token(request: Request) -> str | None:
86
87
  """Extract CSRF token from request headers or form data."""
87
88
  csrf_token = request.headers.get("X-CSRF-Token")
88
89
  if csrf_token:
@@ -151,8 +152,8 @@ def token_security(enforce_https: bool = True, check_csrf: bool = True):
151
152
 
152
153
  def rate_limit_auth(
153
154
  endpoint: str = "login",
154
- max_attempts: Optional[int] = None,
155
- window_seconds: Optional[int] = None,
155
+ max_attempts: int | None = None,
156
+ window_seconds: int | None = None,
156
157
  ):
157
158
  """
158
159
  Rate limiting decorator for auth endpoints.
@@ -242,7 +243,7 @@ def rate_limit_auth(
242
243
  return decorator
243
244
 
244
245
 
245
- def auto_token_setup(func: Optional[Callable[..., Awaitable[Any]]] = None):
246
+ def auto_token_setup(func: Callable[..., Awaitable[Any]] | None = None):
246
247
  """
247
248
  Decorator to automatically set up tokens on successful login/register.
248
249