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.
- mdb_engine/__init__.py +38 -6
- mdb_engine/auth/README.md +534 -11
- mdb_engine/auth/__init__.py +129 -28
- mdb_engine/auth/audit.py +592 -0
- mdb_engine/auth/casbin_factory.py +10 -14
- mdb_engine/auth/config_helpers.py +7 -6
- mdb_engine/auth/cookie_utils.py +3 -7
- mdb_engine/auth/csrf.py +373 -0
- mdb_engine/auth/decorators.py +3 -10
- mdb_engine/auth/dependencies.py +37 -45
- mdb_engine/auth/helpers.py +3 -3
- mdb_engine/auth/integration.py +30 -73
- mdb_engine/auth/jwt.py +2 -6
- mdb_engine/auth/middleware.py +77 -34
- mdb_engine/auth/oso_factory.py +16 -36
- mdb_engine/auth/provider.py +17 -38
- mdb_engine/auth/rate_limiter.py +504 -0
- mdb_engine/auth/restrictions.py +8 -24
- mdb_engine/auth/session_manager.py +14 -29
- mdb_engine/auth/shared_middleware.py +600 -0
- mdb_engine/auth/shared_users.py +759 -0
- mdb_engine/auth/token_store.py +14 -28
- mdb_engine/auth/users.py +54 -113
- mdb_engine/auth/utils.py +213 -15
- mdb_engine/cli/commands/generate.py +545 -9
- mdb_engine/cli/commands/validate.py +3 -7
- mdb_engine/cli/utils.py +3 -3
- mdb_engine/config.py +7 -21
- mdb_engine/constants.py +65 -0
- mdb_engine/core/README.md +117 -6
- mdb_engine/core/__init__.py +39 -7
- mdb_engine/core/app_registration.py +22 -41
- mdb_engine/core/app_secrets.py +290 -0
- mdb_engine/core/connection.py +18 -9
- mdb_engine/core/encryption.py +223 -0
- mdb_engine/core/engine.py +758 -95
- mdb_engine/core/index_management.py +12 -16
- mdb_engine/core/manifest.py +424 -135
- mdb_engine/core/ray_integration.py +435 -0
- mdb_engine/core/seeding.py +10 -18
- mdb_engine/core/service_initialization.py +12 -23
- mdb_engine/core/types.py +2 -5
- mdb_engine/database/README.md +112 -16
- mdb_engine/database/__init__.py +17 -6
- mdb_engine/database/abstraction.py +25 -37
- mdb_engine/database/connection.py +11 -18
- mdb_engine/database/query_validator.py +367 -0
- mdb_engine/database/resource_limiter.py +204 -0
- mdb_engine/database/scoped_wrapper.py +713 -196
- mdb_engine/embeddings/__init__.py +17 -9
- mdb_engine/embeddings/dependencies.py +1 -3
- mdb_engine/embeddings/service.py +11 -25
- mdb_engine/exceptions.py +92 -0
- mdb_engine/indexes/README.md +30 -13
- mdb_engine/indexes/__init__.py +1 -0
- mdb_engine/indexes/helpers.py +1 -1
- mdb_engine/indexes/manager.py +50 -114
- mdb_engine/memory/README.md +2 -2
- mdb_engine/memory/__init__.py +1 -2
- mdb_engine/memory/service.py +30 -87
- mdb_engine/observability/README.md +4 -2
- mdb_engine/observability/__init__.py +26 -9
- mdb_engine/observability/health.py +8 -9
- mdb_engine/observability/metrics.py +32 -12
- mdb_engine/routing/README.md +1 -1
- mdb_engine/routing/__init__.py +1 -3
- mdb_engine/routing/websockets.py +25 -60
- mdb_engine-0.1.7.dist-info/METADATA +285 -0
- mdb_engine-0.1.7.dist-info/RECORD +85 -0
- mdb_engine-0.1.6.dist-info/METADATA +0 -213
- mdb_engine-0.1.6.dist-info/RECORD +0 -75
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.1.7.dist-info}/WHEEL +0 -0
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.1.7.dist-info}/entry_points.txt +0 -0
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.1.7.dist-info}/licenses/LICENSE +0 -0
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.1.7.dist-info}/top_level.txt +0 -0
mdb_engine/auth/restrictions.py
CHANGED
|
@@ -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 (
|
|
17
|
-
|
|
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:
|