mdb-engine 0.1.7__tar.gz → 0.2.1__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.1.7/mdb_engine.egg-info → mdb_engine-0.2.1}/PKG-INFO +42 -14
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/README.md +41 -13
- mdb_engine-0.2.1/mdb_engine/__init__.py +130 -0
- mdb_engine-0.2.1/mdb_engine/auth/ARCHITECTURE.md +112 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/README.md +125 -11
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/__init__.py +7 -1
- mdb_engine-0.2.1/mdb_engine/auth/base.py +252 -0
- mdb_engine-0.2.1/mdb_engine/auth/casbin_factory.py +394 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/dependencies.py +10 -5
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/integration.py +23 -7
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/oso_factory.py +2 -2
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/provider.py +263 -143
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/core/engine.py +307 -6
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/core/manifest.py +35 -15
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/database/README.md +28 -1
- mdb_engine-0.2.1/mdb_engine/dependencies.py +426 -0
- mdb_engine-0.2.1/mdb_engine/di/__init__.py +34 -0
- mdb_engine-0.2.1/mdb_engine/di/container.py +248 -0
- mdb_engine-0.2.1/mdb_engine/di/providers.py +205 -0
- mdb_engine-0.2.1/mdb_engine/di/scopes.py +139 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/embeddings/README.md +54 -24
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/embeddings/__init__.py +22 -23
- mdb_engine-0.2.1/mdb_engine/embeddings/dependencies.py +76 -0
- mdb_engine-0.2.1/mdb_engine/repositories/__init__.py +34 -0
- mdb_engine-0.2.1/mdb_engine/repositories/base.py +325 -0
- mdb_engine-0.2.1/mdb_engine/repositories/mongo.py +233 -0
- mdb_engine-0.2.1/mdb_engine/repositories/unit_of_work.py +166 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1/mdb_engine.egg-info}/PKG-INFO +42 -14
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine.egg-info/SOURCES.txt +11 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/pyproject.toml +1 -1
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/setup.py +1 -1
- mdb_engine-0.1.7/mdb_engine/__init__.py +0 -69
- mdb_engine-0.1.7/mdb_engine/auth/casbin_factory.py +0 -195
- mdb_engine-0.1.7/mdb_engine/embeddings/dependencies.py +0 -191
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/LICENSE +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/MANIFEST.in +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/README.md +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/audit.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/casbin_models.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/config_defaults.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/config_helpers.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/cookie_utils.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/csrf.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/decorators.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/helpers.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/jwt.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/middleware.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/rate_limiter.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/restrictions.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/session_manager.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/shared_middleware.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/shared_users.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/token_lifecycle.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/token_store.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/users.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/auth/utils.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/cli/__init__.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/cli/commands/__init__.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/cli/commands/generate.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/cli/commands/migrate.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/cli/commands/show.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/cli/commands/validate.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/cli/main.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/cli/utils.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/config.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/constants.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/core/README.md +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/core/__init__.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/core/app_registration.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/core/app_secrets.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/core/connection.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/core/encryption.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/core/index_management.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/core/ray_integration.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/core/seeding.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/core/service_initialization.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/core/types.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/database/__init__.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/database/abstraction.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/database/connection.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/database/query_validator.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/database/resource_limiter.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/database/scoped_wrapper.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/embeddings/service.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/exceptions.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/indexes/README.md +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/indexes/__init__.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/indexes/helpers.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/indexes/manager.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/memory/README.md +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/memory/__init__.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/memory/service.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/observability/README.md +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/observability/__init__.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/observability/health.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/observability/logging.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/observability/metrics.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/routing/README.md +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/routing/__init__.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/routing/websockets.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine/utils/__init__.py +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine.egg-info/dependency_links.txt +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine.egg-info/entry_points.txt +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine.egg-info/requires.txt +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/mdb_engine.egg-info/top_level.txt +0 -0
- {mdb_engine-0.1.7 → mdb_engine-0.2.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mdb-engine
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: MongoDB Engine
|
|
5
5
|
Home-page: https://github.com/ranfysvalle02/mdb-engine
|
|
6
6
|
Author: Your Name
|
|
@@ -85,7 +85,9 @@ pip install mdb-engine
|
|
|
85
85
|
|
|
86
86
|
```python
|
|
87
87
|
from pathlib import Path
|
|
88
|
+
from fastapi import Depends
|
|
88
89
|
from mdb_engine import MongoDBEngine
|
|
90
|
+
from mdb_engine.dependencies import get_scoped_db
|
|
89
91
|
|
|
90
92
|
# 1. Initialize the engine
|
|
91
93
|
engine = MongoDBEngine(
|
|
@@ -96,10 +98,9 @@ engine = MongoDBEngine(
|
|
|
96
98
|
# 2. Create a FastAPI app with automatic lifecycle management
|
|
97
99
|
app = engine.create_app(slug="my_app", manifest=Path("manifest.json"))
|
|
98
100
|
|
|
99
|
-
# 3. Use
|
|
101
|
+
# 3. Use request-scoped dependencies - all queries automatically isolated
|
|
100
102
|
@app.post("/tasks")
|
|
101
|
-
async def create_task(task: dict):
|
|
102
|
-
db = engine.get_scoped_db("my_app")
|
|
103
|
+
async def create_task(task: dict, db=Depends(get_scoped_db)):
|
|
103
104
|
result = await db.tasks.insert_one(task)
|
|
104
105
|
return {"id": str(result.inserted_id)}
|
|
105
106
|
```
|
|
@@ -149,22 +150,29 @@ Supported index types: `regular`, `text`, `vector`, `ttl`, `compound`.
|
|
|
149
150
|
|
|
150
151
|
### 2. CRUD Operations (Auto-Scoped)
|
|
151
152
|
|
|
152
|
-
All database operations are automatically scoped to your app:
|
|
153
|
+
All database operations are automatically scoped to your app. Use `Depends(get_scoped_db)` in route handlers:
|
|
153
154
|
|
|
154
155
|
```python
|
|
155
|
-
|
|
156
|
+
from mdb_engine.dependencies import get_scoped_db
|
|
156
157
|
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
@app.post("/tasks")
|
|
159
|
+
async def create_task(task: dict, db=Depends(get_scoped_db)):
|
|
160
|
+
result = await db.tasks.insert_one(task)
|
|
161
|
+
return {"id": str(result.inserted_id)}
|
|
159
162
|
|
|
160
|
-
|
|
161
|
-
|
|
163
|
+
@app.get("/tasks")
|
|
164
|
+
async def list_tasks(db=Depends(get_scoped_db)):
|
|
165
|
+
return await db.tasks.find({"status": "pending"}).to_list(length=10)
|
|
162
166
|
|
|
163
|
-
|
|
164
|
-
|
|
167
|
+
@app.put("/tasks/{task_id}")
|
|
168
|
+
async def update_task(task_id: str, db=Depends(get_scoped_db)):
|
|
169
|
+
await db.tasks.update_one({"_id": task_id}, {"$set": {"status": "done"}})
|
|
170
|
+
return {"updated": True}
|
|
165
171
|
|
|
166
|
-
|
|
167
|
-
|
|
172
|
+
@app.delete("/tasks/{task_id}")
|
|
173
|
+
async def delete_task(task_id: str, db=Depends(get_scoped_db)):
|
|
174
|
+
await db.tasks.delete_one({"_id": task_id})
|
|
175
|
+
return {"deleted": True}
|
|
168
176
|
```
|
|
169
177
|
|
|
170
178
|
**What happens under the hood:**
|
|
@@ -210,6 +218,26 @@ async def health():
|
|
|
210
218
|
| **Multi-App** | Secure cross-app data access | [Multi-App Example](https://github.com/ranfysvalle02/mdb-engine/tree/main/examples/advanced/multi_app) |
|
|
211
219
|
| **SSO** | Shared auth across apps | [Shared Auth Example](https://github.com/ranfysvalle02/mdb-engine/tree/main/examples/advanced/multi_app_shared) |
|
|
212
220
|
|
|
221
|
+
### AppContext — All Services in One Place ✨
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from fastapi import Depends
|
|
225
|
+
from mdb_engine.dependencies import AppContext
|
|
226
|
+
|
|
227
|
+
@app.post("/ai-chat")
|
|
228
|
+
async def chat(query: str, ctx: AppContext = Depends()):
|
|
229
|
+
user = ctx.require_user() # 401 if not logged in
|
|
230
|
+
ctx.require_role("user") # 403 if missing role
|
|
231
|
+
|
|
232
|
+
# Everything available: ctx.db, ctx.embedding_service, ctx.memory, ctx.llm
|
|
233
|
+
if ctx.llm:
|
|
234
|
+
response = ctx.llm.chat.completions.create(
|
|
235
|
+
model=ctx.llm_model,
|
|
236
|
+
messages=[{"role": "user", "content": query}]
|
|
237
|
+
)
|
|
238
|
+
return {"response": response.choices[0].message.content}
|
|
239
|
+
```
|
|
240
|
+
|
|
213
241
|
---
|
|
214
242
|
|
|
215
243
|
## Full Examples
|
|
@@ -20,7 +20,9 @@ pip install mdb-engine
|
|
|
20
20
|
|
|
21
21
|
```python
|
|
22
22
|
from pathlib import Path
|
|
23
|
+
from fastapi import Depends
|
|
23
24
|
from mdb_engine import MongoDBEngine
|
|
25
|
+
from mdb_engine.dependencies import get_scoped_db
|
|
24
26
|
|
|
25
27
|
# 1. Initialize the engine
|
|
26
28
|
engine = MongoDBEngine(
|
|
@@ -31,10 +33,9 @@ engine = MongoDBEngine(
|
|
|
31
33
|
# 2. Create a FastAPI app with automatic lifecycle management
|
|
32
34
|
app = engine.create_app(slug="my_app", manifest=Path("manifest.json"))
|
|
33
35
|
|
|
34
|
-
# 3. Use
|
|
36
|
+
# 3. Use request-scoped dependencies - all queries automatically isolated
|
|
35
37
|
@app.post("/tasks")
|
|
36
|
-
async def create_task(task: dict):
|
|
37
|
-
db = engine.get_scoped_db("my_app")
|
|
38
|
+
async def create_task(task: dict, db=Depends(get_scoped_db)):
|
|
38
39
|
result = await db.tasks.insert_one(task)
|
|
39
40
|
return {"id": str(result.inserted_id)}
|
|
40
41
|
```
|
|
@@ -84,22 +85,29 @@ Supported index types: `regular`, `text`, `vector`, `ttl`, `compound`.
|
|
|
84
85
|
|
|
85
86
|
### 2. CRUD Operations (Auto-Scoped)
|
|
86
87
|
|
|
87
|
-
All database operations are automatically scoped to your app:
|
|
88
|
+
All database operations are automatically scoped to your app. Use `Depends(get_scoped_db)` in route handlers:
|
|
88
89
|
|
|
89
90
|
```python
|
|
90
|
-
|
|
91
|
+
from mdb_engine.dependencies import get_scoped_db
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
@app.post("/tasks")
|
|
94
|
+
async def create_task(task: dict, db=Depends(get_scoped_db)):
|
|
95
|
+
result = await db.tasks.insert_one(task)
|
|
96
|
+
return {"id": str(result.inserted_id)}
|
|
94
97
|
|
|
95
|
-
|
|
96
|
-
|
|
98
|
+
@app.get("/tasks")
|
|
99
|
+
async def list_tasks(db=Depends(get_scoped_db)):
|
|
100
|
+
return await db.tasks.find({"status": "pending"}).to_list(length=10)
|
|
97
101
|
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
@app.put("/tasks/{task_id}")
|
|
103
|
+
async def update_task(task_id: str, db=Depends(get_scoped_db)):
|
|
104
|
+
await db.tasks.update_one({"_id": task_id}, {"$set": {"status": "done"}})
|
|
105
|
+
return {"updated": True}
|
|
100
106
|
|
|
101
|
-
|
|
102
|
-
|
|
107
|
+
@app.delete("/tasks/{task_id}")
|
|
108
|
+
async def delete_task(task_id: str, db=Depends(get_scoped_db)):
|
|
109
|
+
await db.tasks.delete_one({"_id": task_id})
|
|
110
|
+
return {"deleted": True}
|
|
103
111
|
```
|
|
104
112
|
|
|
105
113
|
**What happens under the hood:**
|
|
@@ -145,6 +153,26 @@ async def health():
|
|
|
145
153
|
| **Multi-App** | Secure cross-app data access | [Multi-App Example](https://github.com/ranfysvalle02/mdb-engine/tree/main/examples/advanced/multi_app) |
|
|
146
154
|
| **SSO** | Shared auth across apps | [Shared Auth Example](https://github.com/ranfysvalle02/mdb-engine/tree/main/examples/advanced/multi_app_shared) |
|
|
147
155
|
|
|
156
|
+
### AppContext — All Services in One Place ✨
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from fastapi import Depends
|
|
160
|
+
from mdb_engine.dependencies import AppContext
|
|
161
|
+
|
|
162
|
+
@app.post("/ai-chat")
|
|
163
|
+
async def chat(query: str, ctx: AppContext = Depends()):
|
|
164
|
+
user = ctx.require_user() # 401 if not logged in
|
|
165
|
+
ctx.require_role("user") # 403 if missing role
|
|
166
|
+
|
|
167
|
+
# Everything available: ctx.db, ctx.embedding_service, ctx.memory, ctx.llm
|
|
168
|
+
if ctx.llm:
|
|
169
|
+
response = ctx.llm.chat.completions.create(
|
|
170
|
+
model=ctx.llm_model,
|
|
171
|
+
messages=[{"role": "user", "content": query}]
|
|
172
|
+
)
|
|
173
|
+
return {"response": response.choices[0].message.content}
|
|
174
|
+
```
|
|
175
|
+
|
|
148
176
|
---
|
|
149
177
|
|
|
150
178
|
## Full Examples
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MDB_ENGINE - MongoDB Engine
|
|
3
|
+
|
|
4
|
+
Enterprise-grade engine for building applications with:
|
|
5
|
+
- Automatic database scoping and data isolation
|
|
6
|
+
- Proper dependency injection with service lifetimes
|
|
7
|
+
- Repository pattern for clean data access
|
|
8
|
+
- Authentication and authorization
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
# Simple usage
|
|
12
|
+
from mdb_engine import MongoDBEngine
|
|
13
|
+
engine = MongoDBEngine(mongo_uri=..., db_name=...)
|
|
14
|
+
await engine.initialize()
|
|
15
|
+
db = engine.get_scoped_db("my_app")
|
|
16
|
+
|
|
17
|
+
# With FastAPI integration
|
|
18
|
+
app = engine.create_app(slug="my_app", manifest=Path("manifest.json"))
|
|
19
|
+
|
|
20
|
+
# In routes - use RequestContext for clean DI
|
|
21
|
+
from mdb_engine import RequestContext
|
|
22
|
+
|
|
23
|
+
@app.get("/users/{user_id}")
|
|
24
|
+
async def get_user(user_id: str, ctx: RequestContext = Depends()):
|
|
25
|
+
user = await ctx.uow.users.get(user_id)
|
|
26
|
+
return user
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
# Authentication
|
|
30
|
+
from .auth import AuthorizationProvider, require_admin
|
|
31
|
+
from .auth import get_current_user as auth_get_current_user # noqa: F401
|
|
32
|
+
|
|
33
|
+
# Core MongoDB Engine
|
|
34
|
+
from .core import (
|
|
35
|
+
RAY_AVAILABLE,
|
|
36
|
+
AppRayActor,
|
|
37
|
+
ManifestParser,
|
|
38
|
+
ManifestValidator,
|
|
39
|
+
MongoDBEngine,
|
|
40
|
+
get_ray_actor_handle,
|
|
41
|
+
ray_actor_decorator,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Database layer
|
|
45
|
+
from .database import AppDB, ScopedMongoWrapper
|
|
46
|
+
|
|
47
|
+
# FastAPI dependencies
|
|
48
|
+
from .dependencies import (
|
|
49
|
+
Inject,
|
|
50
|
+
RequestContext,
|
|
51
|
+
get_app_config,
|
|
52
|
+
get_app_slug,
|
|
53
|
+
get_authz_provider,
|
|
54
|
+
get_current_user,
|
|
55
|
+
get_embedding_service,
|
|
56
|
+
get_engine,
|
|
57
|
+
get_llm_client,
|
|
58
|
+
get_llm_model_name,
|
|
59
|
+
get_memory_service,
|
|
60
|
+
get_scoped_db,
|
|
61
|
+
get_unit_of_work,
|
|
62
|
+
get_user_roles,
|
|
63
|
+
inject,
|
|
64
|
+
require_role,
|
|
65
|
+
require_user,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# DI Container
|
|
69
|
+
from .di import Container, Scope, ScopeManager
|
|
70
|
+
|
|
71
|
+
# Index management
|
|
72
|
+
from .indexes import (
|
|
73
|
+
AsyncAtlasIndexManager,
|
|
74
|
+
AutoIndexManager,
|
|
75
|
+
run_index_creation_for_collection,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Repository pattern
|
|
79
|
+
from .repositories import Entity, MongoRepository, Repository, UnitOfWork
|
|
80
|
+
|
|
81
|
+
__version__ = "0.2.1" # Major version bump for new DI system
|
|
82
|
+
|
|
83
|
+
__all__ = [
|
|
84
|
+
# Core Engine
|
|
85
|
+
"MongoDBEngine",
|
|
86
|
+
"ManifestValidator",
|
|
87
|
+
"ManifestParser",
|
|
88
|
+
# Ray Integration (optional)
|
|
89
|
+
"RAY_AVAILABLE",
|
|
90
|
+
"AppRayActor",
|
|
91
|
+
"get_ray_actor_handle",
|
|
92
|
+
"ray_actor_decorator",
|
|
93
|
+
# Database
|
|
94
|
+
"ScopedMongoWrapper",
|
|
95
|
+
"AppDB",
|
|
96
|
+
# DI Container
|
|
97
|
+
"Container",
|
|
98
|
+
"Scope",
|
|
99
|
+
"ScopeManager",
|
|
100
|
+
# Repository Pattern
|
|
101
|
+
"Repository",
|
|
102
|
+
"MongoRepository",
|
|
103
|
+
"Entity",
|
|
104
|
+
"UnitOfWork",
|
|
105
|
+
# Auth
|
|
106
|
+
"AuthorizationProvider",
|
|
107
|
+
"require_admin",
|
|
108
|
+
# FastAPI Dependencies
|
|
109
|
+
"RequestContext",
|
|
110
|
+
"get_engine",
|
|
111
|
+
"get_app_slug",
|
|
112
|
+
"get_app_config",
|
|
113
|
+
"get_scoped_db",
|
|
114
|
+
"get_unit_of_work",
|
|
115
|
+
"get_embedding_service",
|
|
116
|
+
"get_memory_service",
|
|
117
|
+
"get_llm_client",
|
|
118
|
+
"get_llm_model_name",
|
|
119
|
+
"get_authz_provider",
|
|
120
|
+
"get_current_user",
|
|
121
|
+
"get_user_roles",
|
|
122
|
+
"require_user",
|
|
123
|
+
"require_role",
|
|
124
|
+
"inject",
|
|
125
|
+
"Inject",
|
|
126
|
+
# Indexes
|
|
127
|
+
"AsyncAtlasIndexManager",
|
|
128
|
+
"AutoIndexManager",
|
|
129
|
+
"run_index_creation_for_collection",
|
|
130
|
+
]
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Authorization Provider Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The authorization system uses the **Adapter Pattern** with a strict abstract base class to ensure type safety, fail-closed security, and proper abstraction from third-party libraries.
|
|
6
|
+
|
|
7
|
+
## Design Principles
|
|
8
|
+
|
|
9
|
+
1. **Adapter Pattern**: We wrap third-party libraries (Casbin, OSO) without modifying their source code
|
|
10
|
+
2. **Fail-Closed Security**: If authorization evaluation fails, access is denied (never granted)
|
|
11
|
+
3. **Type Safety**: Clear contracts with proper type hints and abstract methods
|
|
12
|
+
4. **Interface Segregation**: Application code only needs `check()`, not engine internals
|
|
13
|
+
5. **Observability**: Structured logging for all authorization decisions and errors
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
BaseAuthorizationProvider (ABC)
|
|
19
|
+
├── CasbinAdapter
|
|
20
|
+
│ └── Wraps casbin.AsyncEnforcer
|
|
21
|
+
└── OsoAdapter
|
|
22
|
+
└── Wraps oso_cloud.Client or oso.Oso
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Base Class: `BaseAuthorizationProvider`
|
|
26
|
+
|
|
27
|
+
Defines the contract that all authorization providers must implement:
|
|
28
|
+
|
|
29
|
+
- `check(subject, resource, action)` - Primary authorization decision method
|
|
30
|
+
- `add_policy(*params)` - Add policy rules
|
|
31
|
+
- `add_role_for_user(*params)` - Assign roles to users
|
|
32
|
+
- `save_policy()` - Persist policies to storage
|
|
33
|
+
- `has_policy(*params)` - Check if policy exists
|
|
34
|
+
- `has_role_for_user(*params)` - Check if user has role
|
|
35
|
+
- `clear_cache()` - Clear authorization cache
|
|
36
|
+
|
|
37
|
+
### Fail-Closed Security
|
|
38
|
+
|
|
39
|
+
All `check()` implementations must:
|
|
40
|
+
1. Catch all exceptions during evaluation
|
|
41
|
+
2. Log errors with full context
|
|
42
|
+
3. Return `False` (deny access) on any error
|
|
43
|
+
4. Never raise exceptions from evaluation failures
|
|
44
|
+
|
|
45
|
+
### Error Handling
|
|
46
|
+
|
|
47
|
+
- **Evaluation Errors**: Handled by `_handle_evaluation_error()` - denies access, logs critically
|
|
48
|
+
- **Operation Errors**: Handled by `_handle_operation_error()` - returns False, logs warning
|
|
49
|
+
|
|
50
|
+
## CasbinAdapter
|
|
51
|
+
|
|
52
|
+
Wraps `casbin.AsyncEnforcer` and handles:
|
|
53
|
+
- Casbin's `(subject, object, action)` format
|
|
54
|
+
- Thread pool execution to prevent blocking
|
|
55
|
+
- Caching for performance
|
|
56
|
+
- MongoDB persistence via MotorAdapter
|
|
57
|
+
|
|
58
|
+
### Format Mapping
|
|
59
|
+
|
|
60
|
+
- `check(subject, resource, action)` → `enforcer.enforce(subject, resource, action)`
|
|
61
|
+
- `add_policy(role, resource, action)` → `enforcer.add_policy(role, resource, action)`
|
|
62
|
+
- `add_role_for_user(user, role)` → `enforcer.add_role_for_user(user, role)`
|
|
63
|
+
|
|
64
|
+
## OsoAdapter
|
|
65
|
+
|
|
66
|
+
Wraps OSO Cloud client and handles:
|
|
67
|
+
- OSO's `authorize(actor, action, resource)` format
|
|
68
|
+
- Type marshalling (strings → TypedObject)
|
|
69
|
+
- Thread pool execution
|
|
70
|
+
- Caching for performance
|
|
71
|
+
|
|
72
|
+
### Format Mapping
|
|
73
|
+
|
|
74
|
+
- `check(subject, resource, action)` → `client.authorize(TypedObject("User", subject), action, TypedObject("Document", resource))`
|
|
75
|
+
- `add_policy(role, resource, action)` → `client.insert(["grants_permission", role, action, resource])`
|
|
76
|
+
- `add_role_for_user(user, role, [resource])` → `client.insert(["has_role", Value("User", user), role, Value("Document", resource)])`
|
|
77
|
+
|
|
78
|
+
## Backward Compatibility
|
|
79
|
+
|
|
80
|
+
The `AuthorizationProvider` Protocol is maintained for backward compatibility. All adapters implement both:
|
|
81
|
+
- The Protocol (for structural typing)
|
|
82
|
+
- The BaseAuthorizationProvider ABC (for inheritance)
|
|
83
|
+
|
|
84
|
+
## Usage
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from mdb_engine.auth import BaseAuthorizationProvider, CasbinAdapter, OsoAdapter
|
|
88
|
+
|
|
89
|
+
# Type checking works with both Protocol and ABC
|
|
90
|
+
async def check_permission(
|
|
91
|
+
provider: BaseAuthorizationProvider, # or AuthorizationProvider
|
|
92
|
+
user: str,
|
|
93
|
+
resource: str,
|
|
94
|
+
action: str,
|
|
95
|
+
) -> bool:
|
|
96
|
+
return await provider.check(user, resource, action)
|
|
97
|
+
|
|
98
|
+
# Runtime type checking
|
|
99
|
+
if provider.is_casbin():
|
|
100
|
+
# Casbin-specific operations
|
|
101
|
+
enforcer = provider._enforcer # Still accessible
|
|
102
|
+
elif provider.is_oso():
|
|
103
|
+
# OSO-specific operations
|
|
104
|
+
client = provider._oso # Still accessible
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Migration Notes
|
|
108
|
+
|
|
109
|
+
- Existing code using `AuthorizationProvider` Protocol continues to work
|
|
110
|
+
- Code checking `hasattr(provider, "_enforcer")` continues to work
|
|
111
|
+
- New code should use `BaseAuthorizationProvider` for better type safety
|
|
112
|
+
- Helper methods `is_casbin()` and `is_oso()` available for type checking
|
|
@@ -926,18 +926,132 @@ CSRF protection is **auto-enabled for shared auth mode**. The middleware uses th
|
|
|
926
926
|
4. SameSite=Lax cookies provide additional protection
|
|
927
927
|
|
|
928
928
|
**Frontend Integration:**
|
|
929
|
+
|
|
930
|
+
Helper function for reading cookies:
|
|
931
|
+
|
|
932
|
+
```javascript
|
|
933
|
+
// Reusable cookie helper
|
|
934
|
+
function getCookie(name) {
|
|
935
|
+
const value = `; ${document.cookie}`;
|
|
936
|
+
const parts = value.split(`; ${name}=`);
|
|
937
|
+
if (parts.length === 2) return parts.pop().split(';').shift();
|
|
938
|
+
return null;
|
|
939
|
+
}
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
Include in all state-changing requests:
|
|
943
|
+
|
|
944
|
+
```javascript
|
|
945
|
+
// POST request with CSRF token
|
|
946
|
+
async function createItem(data) {
|
|
947
|
+
const response = await fetch('/api/items', {
|
|
948
|
+
method: 'POST',
|
|
949
|
+
headers: {
|
|
950
|
+
'Content-Type': 'application/json',
|
|
951
|
+
'X-CSRF-Token': getCookie('csrf_token')
|
|
952
|
+
},
|
|
953
|
+
credentials: 'same-origin',
|
|
954
|
+
body: JSON.stringify(data)
|
|
955
|
+
});
|
|
956
|
+
return response.json();
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// DELETE request with CSRF token
|
|
960
|
+
async function deleteItem(id) {
|
|
961
|
+
const response = await fetch(`/api/items/${id}`, {
|
|
962
|
+
method: 'DELETE',
|
|
963
|
+
headers: {
|
|
964
|
+
'X-CSRF-Token': getCookie('csrf_token')
|
|
965
|
+
},
|
|
966
|
+
credentials: 'same-origin'
|
|
967
|
+
});
|
|
968
|
+
return response.json();
|
|
969
|
+
}
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
**Logout Must Be POST:**
|
|
973
|
+
|
|
974
|
+
For security, logout endpoints should use POST method, not GET:
|
|
975
|
+
|
|
976
|
+
```javascript
|
|
977
|
+
// Correct: POST with CSRF token
|
|
978
|
+
async function logout() {
|
|
979
|
+
const response = await fetch('/logout', {
|
|
980
|
+
method: 'POST',
|
|
981
|
+
headers: {
|
|
982
|
+
'X-CSRF-Token': getCookie('csrf_token')
|
|
983
|
+
},
|
|
984
|
+
credentials: 'same-origin'
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
const result = await response.json();
|
|
988
|
+
if (result.success) {
|
|
989
|
+
window.location.href = result.redirect || '/login';
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
Backend endpoint:
|
|
995
|
+
|
|
996
|
+
```python
|
|
997
|
+
@app.post("/logout")
|
|
998
|
+
async def logout(request: Request):
|
|
999
|
+
"""Logout must be POST with CSRF token."""
|
|
1000
|
+
response = JSONResponse({"success": True, "redirect": "/login"})
|
|
1001
|
+
response = await logout_user(request, response)
|
|
1002
|
+
return response
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
**Login/Register JSON Pattern:**
|
|
1006
|
+
|
|
1007
|
+
Return JSON responses for AJAX forms:
|
|
1008
|
+
|
|
1009
|
+
```python
|
|
1010
|
+
@app.post("/login")
|
|
1011
|
+
async def login(request: Request, email: str = Form(...), password: str = Form(...)):
|
|
1012
|
+
"""Login returning JSON for JavaScript frontend."""
|
|
1013
|
+
result = await authenticate_user(email, password)
|
|
1014
|
+
|
|
1015
|
+
if result["success"]:
|
|
1016
|
+
json_response = JSONResponse({"success": True, "redirect": "/dashboard"})
|
|
1017
|
+
# Copy auth cookies from result
|
|
1018
|
+
for key, value in result["response"].headers.items():
|
|
1019
|
+
if key.lower() == "set-cookie":
|
|
1020
|
+
json_response.headers.append(key, value)
|
|
1021
|
+
return json_response
|
|
1022
|
+
|
|
1023
|
+
return JSONResponse(
|
|
1024
|
+
{"success": False, "detail": result.get("error", "Login failed")},
|
|
1025
|
+
status_code=401
|
|
1026
|
+
)
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
**Error Handling:**
|
|
1030
|
+
|
|
1031
|
+
Handle CSRF validation failures (403 status):
|
|
1032
|
+
|
|
929
1033
|
```javascript
|
|
930
|
-
|
|
931
|
-
const
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1034
|
+
async function secureRequest(url, options = {}) {
|
|
1035
|
+
const response = await fetch(url, {
|
|
1036
|
+
...options,
|
|
1037
|
+
headers: {
|
|
1038
|
+
...options.headers,
|
|
1039
|
+
'X-CSRF-Token': getCookie('csrf_token')
|
|
1040
|
+
},
|
|
1041
|
+
credentials: 'same-origin'
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
if (response.status === 403) {
|
|
1045
|
+
const data = await response.json();
|
|
1046
|
+
if (data.detail?.includes('CSRF')) {
|
|
1047
|
+
// Token expired - refresh the page
|
|
1048
|
+
window.location.reload();
|
|
1049
|
+
return null;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
return response;
|
|
1054
|
+
}
|
|
941
1055
|
```
|
|
942
1056
|
|
|
943
1057
|
### HSTS (HTTP Strict Transport Security)
|
|
@@ -9,6 +9,9 @@ This module is part of MDB_ENGINE - MongoDB Engine.
|
|
|
9
9
|
# Audit logging
|
|
10
10
|
from .audit import AuthAction, AuthAuditLog
|
|
11
11
|
|
|
12
|
+
# Base classes
|
|
13
|
+
from .base import AuthorizationError, BaseAuthorizationProvider
|
|
14
|
+
|
|
12
15
|
# Casbin Factory
|
|
13
16
|
from .casbin_factory import (
|
|
14
17
|
create_casbin_enforcer,
|
|
@@ -123,7 +126,10 @@ from .utils import (
|
|
|
123
126
|
)
|
|
124
127
|
|
|
125
128
|
__all__ = [
|
|
126
|
-
#
|
|
129
|
+
# Base classes
|
|
130
|
+
"BaseAuthorizationProvider",
|
|
131
|
+
"AuthorizationError",
|
|
132
|
+
# Provider (Protocol for backward compatibility)
|
|
127
133
|
"AuthorizationProvider",
|
|
128
134
|
"CasbinAdapter",
|
|
129
135
|
"OsoAdapter",
|