mdb-engine 0.1.6__py3-none-any.whl → 0.4.12__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 +116 -11
- mdb_engine/auth/ARCHITECTURE.md +112 -0
- mdb_engine/auth/README.md +654 -11
- mdb_engine/auth/__init__.py +136 -29
- mdb_engine/auth/audit.py +592 -0
- mdb_engine/auth/base.py +252 -0
- mdb_engine/auth/casbin_factory.py +265 -70
- mdb_engine/auth/config_defaults.py +5 -5
- mdb_engine/auth/config_helpers.py +19 -18
- mdb_engine/auth/cookie_utils.py +12 -16
- mdb_engine/auth/csrf.py +483 -0
- mdb_engine/auth/decorators.py +10 -16
- mdb_engine/auth/dependencies.py +69 -71
- mdb_engine/auth/helpers.py +3 -3
- mdb_engine/auth/integration.py +61 -88
- mdb_engine/auth/jwt.py +11 -15
- mdb_engine/auth/middleware.py +79 -35
- mdb_engine/auth/oso_factory.py +21 -41
- mdb_engine/auth/provider.py +270 -171
- mdb_engine/auth/rate_limiter.py +505 -0
- mdb_engine/auth/restrictions.py +21 -36
- mdb_engine/auth/session_manager.py +24 -41
- mdb_engine/auth/shared_middleware.py +977 -0
- mdb_engine/auth/shared_users.py +775 -0
- mdb_engine/auth/token_lifecycle.py +10 -12
- mdb_engine/auth/token_store.py +17 -32
- mdb_engine/auth/users.py +99 -159
- mdb_engine/auth/utils.py +236 -42
- mdb_engine/cli/commands/generate.py +546 -10
- mdb_engine/cli/commands/validate.py +3 -7
- mdb_engine/cli/utils.py +7 -7
- mdb_engine/config.py +13 -28
- 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 +31 -50
- mdb_engine/core/app_secrets.py +289 -0
- mdb_engine/core/connection.py +20 -12
- mdb_engine/core/encryption.py +222 -0
- mdb_engine/core/engine.py +2862 -115
- mdb_engine/core/index_management.py +12 -16
- mdb_engine/core/manifest.py +628 -204
- mdb_engine/core/ray_integration.py +436 -0
- mdb_engine/core/seeding.py +13 -21
- mdb_engine/core/service_initialization.py +20 -30
- mdb_engine/core/types.py +40 -43
- mdb_engine/database/README.md +140 -17
- mdb_engine/database/__init__.py +17 -6
- mdb_engine/database/abstraction.py +37 -50
- mdb_engine/database/connection.py +51 -30
- mdb_engine/database/query_validator.py +367 -0
- mdb_engine/database/resource_limiter.py +204 -0
- mdb_engine/database/scoped_wrapper.py +747 -237
- mdb_engine/dependencies.py +427 -0
- mdb_engine/di/__init__.py +34 -0
- mdb_engine/di/container.py +247 -0
- mdb_engine/di/providers.py +206 -0
- mdb_engine/di/scopes.py +139 -0
- mdb_engine/embeddings/README.md +54 -24
- mdb_engine/embeddings/__init__.py +31 -24
- mdb_engine/embeddings/dependencies.py +38 -155
- mdb_engine/embeddings/service.py +78 -75
- mdb_engine/exceptions.py +104 -12
- mdb_engine/indexes/README.md +30 -13
- mdb_engine/indexes/__init__.py +1 -0
- mdb_engine/indexes/helpers.py +11 -11
- mdb_engine/indexes/manager.py +59 -123
- mdb_engine/memory/README.md +95 -4
- mdb_engine/memory/__init__.py +1 -2
- mdb_engine/memory/service.py +363 -1168
- mdb_engine/observability/README.md +4 -2
- mdb_engine/observability/__init__.py +26 -9
- mdb_engine/observability/health.py +17 -17
- mdb_engine/observability/logging.py +10 -10
- mdb_engine/observability/metrics.py +40 -19
- mdb_engine/repositories/__init__.py +34 -0
- mdb_engine/repositories/base.py +325 -0
- mdb_engine/repositories/mongo.py +233 -0
- mdb_engine/repositories/unit_of_work.py +166 -0
- mdb_engine/routing/README.md +1 -1
- mdb_engine/routing/__init__.py +1 -3
- mdb_engine/routing/websockets.py +41 -75
- mdb_engine/utils/__init__.py +3 -1
- mdb_engine/utils/mongo.py +117 -0
- mdb_engine-0.4.12.dist-info/METADATA +492 -0
- mdb_engine-0.4.12.dist-info/RECORD +97 -0
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.4.12.dist-info}/WHEEL +1 -1
- 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.4.12.dist-info}/entry_points.txt +0 -0
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.4.12.dist-info}/licenses/LICENSE +0 -0
- {mdb_engine-0.1.6.dist-info → mdb_engine-0.4.12.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Resource limiting for MongoDB Engine.
|
|
3
|
+
|
|
4
|
+
This module provides resource limit enforcement to prevent resource exhaustion
|
|
5
|
+
and ensure fair resource usage across applications.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Query timeout enforcement
|
|
9
|
+
- Result size limits
|
|
10
|
+
- Document size validation
|
|
11
|
+
- Connection limit tracking
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from bson import encode as bson_encode
|
|
18
|
+
from bson.errors import InvalidDocument
|
|
19
|
+
|
|
20
|
+
from ..constants import (
|
|
21
|
+
DEFAULT_MAX_TIME_MS,
|
|
22
|
+
MAX_CURSOR_BATCH_SIZE,
|
|
23
|
+
MAX_DOCUMENT_SIZE,
|
|
24
|
+
MAX_QUERY_RESULT_SIZE,
|
|
25
|
+
MAX_QUERY_TIME_MS,
|
|
26
|
+
)
|
|
27
|
+
from ..exceptions import ResourceLimitExceeded
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ResourceLimiter:
|
|
33
|
+
"""
|
|
34
|
+
Enforces resource limits on MongoDB operations.
|
|
35
|
+
|
|
36
|
+
This class provides resource limit enforcement to prevent:
|
|
37
|
+
- Query timeouts
|
|
38
|
+
- Excessive result sizes
|
|
39
|
+
- Oversized documents
|
|
40
|
+
- Resource exhaustion
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
default_timeout_ms: int = DEFAULT_MAX_TIME_MS,
|
|
46
|
+
max_timeout_ms: int = MAX_QUERY_TIME_MS,
|
|
47
|
+
max_result_size: int = MAX_QUERY_RESULT_SIZE,
|
|
48
|
+
max_batch_size: int = MAX_CURSOR_BATCH_SIZE,
|
|
49
|
+
max_document_size: int = MAX_DOCUMENT_SIZE,
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
Initialize the resource limiter.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
default_timeout_ms: Default query timeout in milliseconds
|
|
56
|
+
max_timeout_ms: Maximum allowed query timeout in milliseconds
|
|
57
|
+
max_result_size: Maximum number of documents in a result set
|
|
58
|
+
max_batch_size: Maximum batch size for cursor operations
|
|
59
|
+
max_document_size: Maximum document size in bytes
|
|
60
|
+
"""
|
|
61
|
+
self.default_timeout_ms = default_timeout_ms
|
|
62
|
+
self.max_timeout_ms = max_timeout_ms
|
|
63
|
+
self.max_result_size = max_result_size
|
|
64
|
+
self.max_batch_size = max_batch_size
|
|
65
|
+
self.max_document_size = max_document_size
|
|
66
|
+
|
|
67
|
+
def enforce_query_timeout(
|
|
68
|
+
self, kwargs: dict[str, Any], default_timeout: int | None = None
|
|
69
|
+
) -> dict[str, Any]:
|
|
70
|
+
"""
|
|
71
|
+
Enforce query timeout by adding maxTimeMS if not present.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
kwargs: Query keyword arguments
|
|
75
|
+
default_timeout: Default timeout to use (defaults to self.default_timeout_ms)
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Updated kwargs with maxTimeMS added if needed
|
|
79
|
+
"""
|
|
80
|
+
kwargs = dict(kwargs) # Create a copy to avoid mutating original
|
|
81
|
+
|
|
82
|
+
default = default_timeout if default_timeout is not None else self.default_timeout_ms
|
|
83
|
+
|
|
84
|
+
# Check if maxTimeMS is already set
|
|
85
|
+
if "maxTimeMS" in kwargs:
|
|
86
|
+
user_timeout = kwargs["maxTimeMS"]
|
|
87
|
+
# Validate user-provided timeout doesn't exceed maximum
|
|
88
|
+
if user_timeout > self.max_timeout_ms:
|
|
89
|
+
logger.warning(
|
|
90
|
+
f"Query timeout {user_timeout}ms exceeds maximum {self.max_timeout_ms}ms. "
|
|
91
|
+
f"Capping to {self.max_timeout_ms}ms"
|
|
92
|
+
)
|
|
93
|
+
kwargs["maxTimeMS"] = self.max_timeout_ms
|
|
94
|
+
else:
|
|
95
|
+
# Add default timeout
|
|
96
|
+
kwargs["maxTimeMS"] = default
|
|
97
|
+
|
|
98
|
+
return kwargs
|
|
99
|
+
|
|
100
|
+
def enforce_result_limit(self, limit: int | None, max_limit: int | None = None) -> int:
|
|
101
|
+
"""
|
|
102
|
+
Enforce maximum result limit.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
limit: Requested limit (None means no limit)
|
|
106
|
+
max_limit: Maximum allowed limit (defaults to self.max_result_size)
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Enforced limit value (capped to maximum if needed)
|
|
110
|
+
"""
|
|
111
|
+
max_allowed = max_limit if max_limit is not None else self.max_result_size
|
|
112
|
+
|
|
113
|
+
if limit is None:
|
|
114
|
+
# No limit requested, return max allowed
|
|
115
|
+
return max_allowed
|
|
116
|
+
|
|
117
|
+
if limit > max_allowed:
|
|
118
|
+
logger.warning(
|
|
119
|
+
f"Result limit {limit} exceeds maximum {max_allowed}. " f"Capping to {max_allowed}"
|
|
120
|
+
)
|
|
121
|
+
return max_allowed
|
|
122
|
+
|
|
123
|
+
return limit
|
|
124
|
+
|
|
125
|
+
def enforce_batch_size(self, batch_size: int | None, max_batch: int | None = None) -> int:
|
|
126
|
+
"""
|
|
127
|
+
Enforce maximum batch size for cursor operations.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
batch_size: Requested batch size (None means use default)
|
|
131
|
+
max_batch: Maximum allowed batch size (defaults to self.max_batch_size)
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Enforced batch size
|
|
135
|
+
"""
|
|
136
|
+
max_allowed = max_batch if max_batch is not None else self.max_batch_size
|
|
137
|
+
|
|
138
|
+
if batch_size is None:
|
|
139
|
+
return max_allowed
|
|
140
|
+
|
|
141
|
+
if batch_size > max_allowed:
|
|
142
|
+
logger.warning(
|
|
143
|
+
f"Batch size {batch_size} exceeds maximum {max_allowed}. "
|
|
144
|
+
f"Capping to {max_allowed}"
|
|
145
|
+
)
|
|
146
|
+
return max_allowed
|
|
147
|
+
|
|
148
|
+
return batch_size
|
|
149
|
+
|
|
150
|
+
def validate_document_size(self, document: dict[str, Any]) -> None:
|
|
151
|
+
"""
|
|
152
|
+
Validate that a document doesn't exceed size limits.
|
|
153
|
+
|
|
154
|
+
Uses actual BSON encoding for accurate size calculation.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
document: Document to validate
|
|
158
|
+
|
|
159
|
+
Raises:
|
|
160
|
+
ResourceLimitExceeded: If document exceeds size limit
|
|
161
|
+
"""
|
|
162
|
+
try:
|
|
163
|
+
# Use actual BSON encoding for accurate size
|
|
164
|
+
bson_bytes = bson_encode(document)
|
|
165
|
+
actual_size = len(bson_bytes)
|
|
166
|
+
|
|
167
|
+
if actual_size > self.max_document_size:
|
|
168
|
+
raise ResourceLimitExceeded(
|
|
169
|
+
f"Document size {actual_size} bytes exceeds maximum "
|
|
170
|
+
f"{self.max_document_size} bytes",
|
|
171
|
+
limit_type="document_size",
|
|
172
|
+
limit_value=self.max_document_size,
|
|
173
|
+
actual_value=actual_size,
|
|
174
|
+
)
|
|
175
|
+
except ResourceLimitExceeded:
|
|
176
|
+
# Re-raise our validation exceptions immediately
|
|
177
|
+
raise
|
|
178
|
+
except InvalidDocument as e:
|
|
179
|
+
# If BSON encoding fails, log warning but don't fail
|
|
180
|
+
# MongoDB will catch this anyway during actual insert
|
|
181
|
+
logger.warning(f"Could not encode document as BSON for size validation: {e}")
|
|
182
|
+
|
|
183
|
+
def validate_documents_size(self, documents: list[dict[str, Any]]) -> None:
|
|
184
|
+
"""
|
|
185
|
+
Validate that multiple documents don't exceed size limits.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
documents: List of documents to validate
|
|
189
|
+
|
|
190
|
+
Raises:
|
|
191
|
+
ResourceLimitExceeded: If any document exceeds size limit
|
|
192
|
+
"""
|
|
193
|
+
for idx, doc in enumerate(documents):
|
|
194
|
+
try:
|
|
195
|
+
self.validate_document_size(doc)
|
|
196
|
+
except ResourceLimitExceeded as e:
|
|
197
|
+
# Add document index to error context
|
|
198
|
+
raise ResourceLimitExceeded(
|
|
199
|
+
f"{e.message} (document index: {idx})",
|
|
200
|
+
limit_type=e.limit_type,
|
|
201
|
+
limit_value=e.limit_value,
|
|
202
|
+
actual_value=e.actual_value,
|
|
203
|
+
context={**(e.context or {}), "document_index": idx},
|
|
204
|
+
) from e
|