mdb-engine 0.1.6__py3-none-any.whl → 0.1.7__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 (75) hide show
  1. mdb_engine/__init__.py +38 -6
  2. mdb_engine/auth/README.md +534 -11
  3. mdb_engine/auth/__init__.py +129 -28
  4. mdb_engine/auth/audit.py +592 -0
  5. mdb_engine/auth/casbin_factory.py +10 -14
  6. mdb_engine/auth/config_helpers.py +7 -6
  7. mdb_engine/auth/cookie_utils.py +3 -7
  8. mdb_engine/auth/csrf.py +373 -0
  9. mdb_engine/auth/decorators.py +3 -10
  10. mdb_engine/auth/dependencies.py +37 -45
  11. mdb_engine/auth/helpers.py +3 -3
  12. mdb_engine/auth/integration.py +30 -73
  13. mdb_engine/auth/jwt.py +2 -6
  14. mdb_engine/auth/middleware.py +77 -34
  15. mdb_engine/auth/oso_factory.py +16 -36
  16. mdb_engine/auth/provider.py +17 -38
  17. mdb_engine/auth/rate_limiter.py +504 -0
  18. mdb_engine/auth/restrictions.py +8 -24
  19. mdb_engine/auth/session_manager.py +14 -29
  20. mdb_engine/auth/shared_middleware.py +600 -0
  21. mdb_engine/auth/shared_users.py +759 -0
  22. mdb_engine/auth/token_store.py +14 -28
  23. mdb_engine/auth/users.py +54 -113
  24. mdb_engine/auth/utils.py +213 -15
  25. mdb_engine/cli/commands/generate.py +545 -9
  26. mdb_engine/cli/commands/validate.py +3 -7
  27. mdb_engine/cli/utils.py +3 -3
  28. mdb_engine/config.py +7 -21
  29. mdb_engine/constants.py +65 -0
  30. mdb_engine/core/README.md +117 -6
  31. mdb_engine/core/__init__.py +39 -7
  32. mdb_engine/core/app_registration.py +22 -41
  33. mdb_engine/core/app_secrets.py +290 -0
  34. mdb_engine/core/connection.py +18 -9
  35. mdb_engine/core/encryption.py +223 -0
  36. mdb_engine/core/engine.py +758 -95
  37. mdb_engine/core/index_management.py +12 -16
  38. mdb_engine/core/manifest.py +424 -135
  39. mdb_engine/core/ray_integration.py +435 -0
  40. mdb_engine/core/seeding.py +10 -18
  41. mdb_engine/core/service_initialization.py +12 -23
  42. mdb_engine/core/types.py +2 -5
  43. mdb_engine/database/README.md +112 -16
  44. mdb_engine/database/__init__.py +17 -6
  45. mdb_engine/database/abstraction.py +25 -37
  46. mdb_engine/database/connection.py +11 -18
  47. mdb_engine/database/query_validator.py +367 -0
  48. mdb_engine/database/resource_limiter.py +204 -0
  49. mdb_engine/database/scoped_wrapper.py +713 -196
  50. mdb_engine/embeddings/__init__.py +17 -9
  51. mdb_engine/embeddings/dependencies.py +1 -3
  52. mdb_engine/embeddings/service.py +11 -25
  53. mdb_engine/exceptions.py +92 -0
  54. mdb_engine/indexes/README.md +30 -13
  55. mdb_engine/indexes/__init__.py +1 -0
  56. mdb_engine/indexes/helpers.py +1 -1
  57. mdb_engine/indexes/manager.py +50 -114
  58. mdb_engine/memory/README.md +2 -2
  59. mdb_engine/memory/__init__.py +1 -2
  60. mdb_engine/memory/service.py +30 -87
  61. mdb_engine/observability/README.md +4 -2
  62. mdb_engine/observability/__init__.py +26 -9
  63. mdb_engine/observability/health.py +8 -9
  64. mdb_engine/observability/metrics.py +32 -12
  65. mdb_engine/routing/README.md +1 -1
  66. mdb_engine/routing/__init__.py +1 -3
  67. mdb_engine/routing/websockets.py +25 -60
  68. mdb_engine-0.1.7.dist-info/METADATA +285 -0
  69. mdb_engine-0.1.7.dist-info/RECORD +85 -0
  70. mdb_engine-0.1.6.dist-info/METADATA +0 -213
  71. mdb_engine-0.1.6.dist-info/RECORD +0 -75
  72. {mdb_engine-0.1.6.dist-info → mdb_engine-0.1.7.dist-info}/WHEEL +0 -0
  73. {mdb_engine-0.1.6.dist-info → mdb_engine-0.1.7.dist-info}/entry_points.txt +0 -0
  74. {mdb_engine-0.1.6.dist-info → mdb_engine-0.1.7.dist-info}/licenses/LICENSE +0 -0
  75. {mdb_engine-0.1.6.dist-info → mdb_engine-0.1.7.dist-info}/top_level.txt +0 -0
@@ -27,9 +27,7 @@ from .users import get_app_user
27
27
  logger = logging.getLogger(__name__)
28
28
 
29
29
 
30
- def is_demo_user(
31
- user: Optional[Dict[str, Any]] = None, email: Optional[str] = None
32
- ) -> bool:
30
+ def is_demo_user(user: Optional[Dict[str, Any]] = None, email: Optional[str] = None) -> bool:
33
31
  """
34
32
  Check if a user is a demo user.
35
33
 
@@ -85,9 +83,7 @@ async def _get_sub_auth_user(
85
83
  if not (config and users_config.get("enabled", False)):
86
84
  return None
87
85
 
88
- app_user = await get_app_user(
89
- request, slug_id, db, config, allow_demo_fallback=False
90
- )
86
+ app_user = await get_app_user(request, slug_id, db, config, allow_demo_fallback=False)
91
87
  if not app_user:
92
88
  return None
93
89
 
@@ -119,9 +115,7 @@ async def _get_authenticated_user(
119
115
 
120
116
  # Try sub-auth if platform auth didn't work
121
117
  if get_app_db_func and get_app_config_func:
122
- return await _get_sub_auth_user(
123
- request, slug_id, get_app_config_func, get_app_db_func
124
- )
118
+ return await _get_sub_auth_user(request, slug_id, get_app_config_func, get_app_db_func)
125
119
 
126
120
  return None
127
121
 
@@ -158,9 +152,7 @@ def _validate_dependencies(
158
152
 
159
153
  async def require_non_demo_user(
160
154
  request: Request,
161
- get_app_config_func: Optional[
162
- Callable[[Request, str, Dict], Awaitable[Dict]]
163
- ] = None,
155
+ get_app_config_func: Optional[Callable[[Request, str, Dict], Awaitable[Dict]]] = None,
164
156
  get_app_db_func: Optional[Callable[[Request], Awaitable[Any]]] = None,
165
157
  ) -> Dict[str, Any]:
166
158
  """
@@ -189,9 +181,7 @@ async def require_non_demo_user(
189
181
  if get_app_db_func and get_app_config_func:
190
182
  _validate_dependencies(get_app_config_func, get_app_db_func)
191
183
 
192
- user = await _get_authenticated_user(
193
- request, slug_id, get_app_config_func, get_app_db_func
194
- )
184
+ user = await _get_authenticated_user(request, slug_id, get_app_config_func, get_app_db_func)
195
185
 
196
186
  # Check if user is demo
197
187
  if user and is_demo_user(user):
@@ -215,9 +205,7 @@ async def require_non_demo_user(
215
205
 
216
206
  async def block_demo_users(
217
207
  request: Request,
218
- get_app_config_func: Optional[
219
- Callable[[Request, str, Dict], Awaitable[Dict]]
220
- ] = None,
208
+ get_app_config_func: Optional[Callable[[Request, str, Dict], Awaitable[Dict]]] = None,
221
209
  get_app_db_func: Optional[Callable[[Request], Awaitable[Any]]] = None,
222
210
  ):
223
211
  """
@@ -253,15 +241,11 @@ async def block_demo_users(
253
241
 
254
242
  # Check sub-auth if platform auth didn't work
255
243
  if not user and get_app_db_func and get_app_config_func and slug_id:
256
- user = await _get_sub_auth_user(
257
- request, slug_id, get_app_config_func, get_app_db_func
258
- )
244
+ user = await _get_sub_auth_user(request, slug_id, get_app_config_func, get_app_db_func)
259
245
 
260
246
  # Block if demo user (only if user exists)
261
247
  if user and is_demo_user(user):
262
- logger.info(
263
- f"Demo user '{user.get('email')}' blocked from accessing: {request.url.path}"
264
- )
248
+ logger.info(f"Demo user '{user.get('email')}' blocked from accessing: {request.url.path}")
265
249
  raise HTTPException(
266
250
  status_code=status.HTTP_403_FORBIDDEN,
267
251
  detail="Demo users cannot access this endpoint. Demo mode is read-only.",
@@ -13,8 +13,11 @@ from typing import Any, Dict, List, Optional
13
13
  from bson.objectid import ObjectId
14
14
 
15
15
  try:
16
- from pymongo.errors import (ConnectionFailure, OperationFailure,
17
- ServerSelectionTimeoutError)
16
+ from pymongo.errors import (
17
+ ConnectionFailure,
18
+ OperationFailure,
19
+ ServerSelectionTimeoutError,
20
+ )
18
21
  except ImportError:
19
22
  ConnectionFailure = Exception
20
23
  OperationFailure = Exception
@@ -120,9 +123,7 @@ class SessionManager:
120
123
  # Remove oldest inactive session
121
124
  await self.cleanup_inactive_sessions(user_id)
122
125
  # Check again
123
- active_sessions = await self.get_user_sessions(
124
- user_id, active_only=True
125
- )
126
+ active_sessions = await self.get_user_sessions(user_id, active_only=True)
126
127
  if len(active_sessions) >= self.max_sessions:
127
128
  # Force remove oldest session
128
129
  if active_sessions:
@@ -168,9 +169,7 @@ class SessionManager:
168
169
  ValueError,
169
170
  TypeError,
170
171
  ) as e:
171
- logger.error(
172
- f"Error creating session for user {user_id}: {e}", exc_info=True
173
- )
172
+ logger.error(f"Error creating session for user {user_id}: {e}", exc_info=True)
174
173
  return None
175
174
 
176
175
  async def update_session_activity(
@@ -205,14 +204,10 @@ class SessionManager:
205
204
  ValueError,
206
205
  TypeError,
207
206
  ) as e:
208
- logger.error(
209
- f"Error updating session activity for {refresh_jti}: {e}", exc_info=True
210
- )
207
+ logger.error(f"Error updating session activity for {refresh_jti}: {e}", exc_info=True)
211
208
  return False
212
209
 
213
- async def get_session_by_refresh_token(
214
- self, refresh_jti: str
215
- ) -> Optional[Dict[str, Any]]:
210
+ async def get_session_by_refresh_token(self, refresh_jti: str) -> Optional[Dict[str, Any]]:
216
211
  """
217
212
  Get session by refresh token JWT ID.
218
213
 
@@ -223,9 +218,7 @@ class SessionManager:
223
218
  Session document or None if not found
224
219
  """
225
220
  try:
226
- session = await self.collection.find_one(
227
- {"refresh_jti": refresh_jti, "active": True}
228
- )
221
+ session = await self.collection.find_one({"refresh_jti": refresh_jti, "active": True})
229
222
  return session
230
223
  except (
231
224
  OperationFailure,
@@ -267,9 +260,7 @@ class SessionManager:
267
260
  stored_fingerprint = session.get("session_fingerprint")
268
261
 
269
262
  if not stored_fingerprint:
270
- strict_mode = (
271
- strict if strict is not None else self.fingerprinting_strict
272
- )
263
+ strict_mode = strict if strict is not None else self.fingerprinting_strict
273
264
  return not strict_mode
274
265
 
275
266
  return stored_fingerprint == current_fingerprint
@@ -313,9 +304,7 @@ class SessionManager:
313
304
  if active_only:
314
305
  query["active"] = True
315
306
 
316
- sessions = (
317
- await self.collection.find(query).sort("last_seen", -1).to_list(None)
318
- )
307
+ sessions = await self.collection.find(query).sort("last_seen", -1).to_list(None)
319
308
  return sessions
320
309
  except (
321
310
  OperationFailure,
@@ -324,9 +313,7 @@ class SessionManager:
324
313
  ValueError,
325
314
  TypeError,
326
315
  ) as e:
327
- logger.error(
328
- f"Error getting sessions for user {user_id}: {e}", exc_info=True
329
- )
316
+ logger.error(f"Error getting sessions for user {user_id}: {e}", exc_info=True)
330
317
  return []
331
318
 
332
319
  async def revoke_session(self, session_id: Any) -> bool:
@@ -400,9 +387,7 @@ class SessionManager:
400
387
  ValueError,
401
388
  TypeError,
402
389
  ) as e:
403
- logger.error(
404
- f"Error revoking sessions for user {user_id}: {e}", exc_info=True
405
- )
390
+ logger.error(f"Error revoking sessions for user {user_id}: {e}", exc_info=True)
406
391
  return 0
407
392
 
408
393
  async def cleanup_inactive_sessions(self, user_id: Optional[str] = None) -> int: