mdb-engine 0.2.1__tar.gz → 0.2.3__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.
- {mdb_engine-0.2.1/mdb_engine.egg-info → mdb_engine-0.2.3}/PKG-INFO +8 -8
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/audit.py +40 -40
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/base.py +3 -3
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/casbin_factory.py +6 -6
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/config_defaults.py +5 -5
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/config_helpers.py +12 -12
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/cookie_utils.py +9 -9
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/csrf.py +9 -8
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/decorators.py +7 -6
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/dependencies.py +22 -21
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/integration.py +9 -9
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/jwt.py +9 -9
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/middleware.py +4 -3
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/oso_factory.py +6 -6
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/provider.py +4 -4
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/rate_limiter.py +12 -11
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/restrictions.py +16 -15
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/session_manager.py +11 -13
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/shared_middleware.py +16 -15
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/shared_users.py +20 -20
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/token_lifecycle.py +10 -12
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/token_store.py +4 -5
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/users.py +51 -52
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/utils.py +29 -33
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/cli/commands/generate.py +6 -6
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/cli/utils.py +4 -4
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/config.py +6 -7
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/core/app_registration.py +12 -12
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/core/app_secrets.py +1 -2
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/core/connection.py +3 -4
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/core/encryption.py +1 -2
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/core/engine.py +43 -44
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/core/manifest.py +59 -58
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/core/ray_integration.py +10 -9
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/core/seeding.py +3 -3
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/core/service_initialization.py +10 -9
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/core/types.py +40 -40
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/database/abstraction.py +15 -16
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/database/connection.py +40 -12
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/database/query_validator.py +8 -8
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/database/resource_limiter.py +7 -7
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/database/scoped_wrapper.py +51 -58
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/dependencies.py +14 -13
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/di/container.py +12 -13
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/di/providers.py +14 -13
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/di/scopes.py +5 -5
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/embeddings/dependencies.py +2 -2
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/embeddings/service.py +31 -43
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/exceptions.py +20 -20
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/indexes/helpers.py +11 -11
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/indexes/manager.py +9 -9
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/memory/service.py +30 -30
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/observability/health.py +10 -9
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/observability/logging.py +10 -10
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/observability/metrics.py +8 -7
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/repositories/base.py +25 -25
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/repositories/mongo.py +17 -17
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/repositories/unit_of_work.py +6 -6
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/routing/websockets.py +19 -18
- {mdb_engine-0.2.1 → mdb_engine-0.2.3/mdb_engine.egg-info}/PKG-INFO +8 -8
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine.egg-info/requires.txt +3 -2
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/pyproject.toml +10 -9
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/setup.py +6 -7
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/LICENSE +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/MANIFEST.in +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/README.md +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/README.md +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/ARCHITECTURE.md +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/README.md +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/casbin_models.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/auth/helpers.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/cli/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/cli/commands/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/cli/commands/migrate.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/cli/commands/show.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/cli/commands/validate.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/cli/main.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/constants.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/core/README.md +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/core/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/core/index_management.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/database/README.md +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/database/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/di/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/embeddings/README.md +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/embeddings/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/indexes/README.md +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/indexes/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/memory/README.md +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/memory/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/observability/README.md +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/observability/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/repositories/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/routing/README.md +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/routing/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine/utils/__init__.py +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine.egg-info/SOURCES.txt +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine.egg-info/dependency_links.txt +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine.egg-info/entry_points.txt +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/mdb_engine.egg-info/top_level.txt +0 -0
- {mdb_engine-0.2.1 → mdb_engine-0.2.3}/setup.cfg +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mdb-engine
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: MongoDB Engine
|
|
5
5
|
Home-page: https://github.com/ranfysvalle02/mdb-engine
|
|
6
|
-
Author:
|
|
7
|
-
Author-email:
|
|
6
|
+
Author: Fabian Valle
|
|
7
|
+
Author-email: Fabian Valle <oblivio.company@gmail.com>
|
|
8
8
|
License: AGPL-3.0
|
|
9
9
|
Project-URL: Homepage, https://github.com/ranfysvalle02/mdb-engine
|
|
10
10
|
Project-URL: Documentation, https://github.com/ranfysvalle02/mdb-engine#readme
|
|
@@ -15,13 +15,12 @@ Classifier: Development Status :: 4 - Beta
|
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
16
16
|
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
|
17
17
|
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
21
|
Classifier: Topic :: Database
|
|
23
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.10
|
|
25
24
|
Description-Content-Type: text/markdown
|
|
26
25
|
License-File: LICENSE
|
|
27
26
|
Requires-Dist: motor>=3.0.0
|
|
@@ -32,7 +31,7 @@ Requires-Dist: pyjwt>=2.8.0
|
|
|
32
31
|
Requires-Dist: jsonschema>=4.0.0
|
|
33
32
|
Requires-Dist: bcrypt>=4.0.0
|
|
34
33
|
Requires-Dist: cryptography>=41.0.0
|
|
35
|
-
Requires-Dist: mem0ai>=1.
|
|
34
|
+
Requires-Dist: mem0ai>=0.1.7
|
|
36
35
|
Requires-Dist: semantic-text-splitter>=0.9.0
|
|
37
36
|
Requires-Dist: numpy<2.0.0,>=1.0.0
|
|
38
37
|
Requires-Dist: openai>=1.0.0
|
|
@@ -50,9 +49,10 @@ Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
|
|
|
50
49
|
Requires-Dist: pytest-cov>=4.1.0; extra == "test"
|
|
51
50
|
Requires-Dist: pytest-mock>=3.11.0; extra == "test"
|
|
52
51
|
Requires-Dist: pytest-timeout>=2.1.0; extra == "test"
|
|
52
|
+
Requires-Dist: pytest-xdist>=3.3.0; extra == "test"
|
|
53
53
|
Requires-Dist: testcontainers>=3.7.0; extra == "test"
|
|
54
54
|
Provides-Extra: dev
|
|
55
|
-
Requires-Dist: ruff
|
|
55
|
+
Requires-Dist: ruff<0.6.0,>=0.4.0; extra == "dev"
|
|
56
56
|
Requires-Dist: semgrep>=1.50.0; extra == "dev"
|
|
57
57
|
Provides-Extra: all
|
|
58
58
|
Requires-Dist: casbin>=1.0.0; extra == "all"
|
|
@@ -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
|
|
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:
|
|
165
|
-
user_id:
|
|
166
|
-
app_slug:
|
|
167
|
-
ip_address:
|
|
168
|
-
user_agent:
|
|
169
|
-
details:
|
|
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:
|
|
221
|
-
user_agent:
|
|
222
|
-
app_slug:
|
|
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:
|
|
239
|
-
user_agent:
|
|
240
|
-
app_slug:
|
|
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:
|
|
257
|
-
app_slug:
|
|
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:
|
|
272
|
-
user_agent:
|
|
273
|
-
app_slug:
|
|
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:
|
|
290
|
-
new_roles:
|
|
291
|
-
changed_by:
|
|
292
|
-
ip_address:
|
|
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:
|
|
316
|
-
app_slug:
|
|
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:
|
|
333
|
-
app_slug:
|
|
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:
|
|
353
|
-
success:
|
|
352
|
+
action: AuthAction | None = None,
|
|
353
|
+
success: bool | None = None,
|
|
354
354
|
limit: int = 100,
|
|
355
|
-
) ->
|
|
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:
|
|
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:
|
|
388
|
-
ip_address:
|
|
387
|
+
email: str | None = None,
|
|
388
|
+
ip_address: str | None = None,
|
|
389
389
|
hours: int = 24,
|
|
390
390
|
limit: int = 100,
|
|
391
|
-
) ->
|
|
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:
|
|
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
|
-
) ->
|
|
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
|
-
) ->
|
|
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:
|
|
502
|
-
ip_address:
|
|
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:
|
|
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
|
-
) ->
|
|
534
|
+
) -> dict[str, Any]:
|
|
535
535
|
"""
|
|
536
536
|
Get security summary statistics.
|
|
537
537
|
|
|
@@ -11,7 +11,7 @@ from __future__ import annotations
|
|
|
11
11
|
|
|
12
12
|
import abc
|
|
13
13
|
import logging
|
|
14
|
-
from typing import Any
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
) ->
|
|
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,
|
|
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,
|
|
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
|
|
10
|
+
from typing import Any
|
|
11
11
|
|
|
12
|
-
SECURITY_CONFIG_DEFAULTS:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
27
|
-
) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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
|
|
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:
|
|
20
|
-
) ->
|
|
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:
|
|
80
|
-
request:
|
|
81
|
-
config:
|
|
82
|
-
access_token_ttl:
|
|
83
|
-
refresh_token_ttl:
|
|
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:
|
|
133
|
+
def clear_auth_cookies(response, request: Request | None = None):
|
|
134
134
|
"""
|
|
135
135
|
Clear authentication cookies from response.
|
|
136
136
|
|
|
@@ -36,7 +36,8 @@ import logging
|
|
|
36
36
|
import os
|
|
37
37
|
import secrets
|
|
38
38
|
import time
|
|
39
|
-
from
|
|
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:
|
|
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:
|
|
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:
|
|
150
|
-
exempt_routes:
|
|
151
|
-
exempt_methods:
|
|
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:
|
|
304
|
-
secret:
|
|
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
|
|
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:
|
|
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) ->
|
|
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:
|
|
155
|
-
window_seconds:
|
|
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:
|
|
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
|
|