mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-17 10:15:27 -04:00
fix(personal): require document privilege for rag upload (#2990)
This commit is contained in:
@@ -8,7 +8,7 @@ from fastapi import APIRouter, HTTPException, Query, Request, UploadFile, File,
|
|||||||
from src.request_models import DirectoryRequest
|
from src.request_models import DirectoryRequest
|
||||||
from core.constants import BASE_DIR, PERSONAL_DIR
|
from core.constants import BASE_DIR, PERSONAL_DIR
|
||||||
from src.rag_singleton import get_rag_manager
|
from src.rag_singleton import get_rag_manager
|
||||||
from src.auth_helpers import get_current_user, require_user
|
from src.auth_helpers import require_privilege, require_user
|
||||||
from core.middleware import require_admin
|
from core.middleware import require_admin
|
||||||
from src.upload_handler import secure_filename
|
from src.upload_handler import secure_filename
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@ def setup_personal_routes(personal_docs_manager, rag_manager, rag_available):
|
|||||||
@router.post("/upload")
|
@router.post("/upload")
|
||||||
async def upload_files_to_rag(request: Request, files: List[UploadFile] = File(...)):
|
async def upload_files_to_rag(request: Request, files: List[UploadFile] = File(...)):
|
||||||
"""Upload files directly into RAG. Supports text and PDF."""
|
"""Upload files directly into RAG. Supports text and PDF."""
|
||||||
user = get_current_user(request)
|
user = require_privilege(request, "can_use_documents")
|
||||||
rag = _rag()
|
rag = _rag()
|
||||||
if not rag:
|
if not rag:
|
||||||
raise HTTPException(503, "RAG system is not available — is the embedding service running?")
|
raise HTTPException(503, "RAG system is not available — is the embedding service running?")
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import asyncio
|
||||||
|
from pathlib import Path
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from fastapi import HTTPException
|
||||||
|
|
||||||
|
from routes import personal_routes
|
||||||
|
|
||||||
|
|
||||||
|
def _upload_endpoint():
|
||||||
|
router = personal_routes.setup_personal_routes(_FakePersonalDocs(), None, True)
|
||||||
|
for route in router.routes:
|
||||||
|
if getattr(route, "path", "") == "/api/personal/upload" and "POST" in getattr(route, "methods", set()):
|
||||||
|
return route.endpoint
|
||||||
|
raise AssertionError("upload endpoint not found")
|
||||||
|
|
||||||
|
|
||||||
|
def _request(privileges):
|
||||||
|
class _AuthManager:
|
||||||
|
def get_privileges(self, user):
|
||||||
|
assert user == "alice"
|
||||||
|
return privileges
|
||||||
|
|
||||||
|
return SimpleNamespace(
|
||||||
|
state=SimpleNamespace(current_user="alice"),
|
||||||
|
app=SimpleNamespace(
|
||||||
|
state=SimpleNamespace(
|
||||||
|
auth_manager=_AuthManager(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
client=SimpleNamespace(host="203.0.113.10"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class _FakePersonalDocs:
|
||||||
|
def __init__(self):
|
||||||
|
self.added = []
|
||||||
|
|
||||||
|
def add_directory(self, directory, index=False):
|
||||||
|
self.added.append((directory, index))
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeRAG:
|
||||||
|
def __init__(self):
|
||||||
|
self.docs = []
|
||||||
|
|
||||||
|
def _split_into_chunks(self, text, chunk_size=500):
|
||||||
|
return [text]
|
||||||
|
|
||||||
|
def add_document(self, chunk, metadata):
|
||||||
|
self.docs.append((chunk, metadata))
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class _Upload:
|
||||||
|
filename = "notes.txt"
|
||||||
|
|
||||||
|
async def read(self, limit):
|
||||||
|
return b"hello from upload"
|
||||||
|
|
||||||
|
|
||||||
|
def test_personal_upload_requires_document_privilege(monkeypatch):
|
||||||
|
monkeypatch.setenv("AUTH_ENABLED", "true")
|
||||||
|
monkeypatch.setattr(
|
||||||
|
personal_routes,
|
||||||
|
"get_rag_manager",
|
||||||
|
lambda: pytest.fail("RAG must not be touched before privilege passes"),
|
||||||
|
)
|
||||||
|
|
||||||
|
endpoint = _upload_endpoint()
|
||||||
|
|
||||||
|
with pytest.raises(HTTPException) as exc:
|
||||||
|
asyncio.run(endpoint(request=_request({"can_use_documents": False}), files=[]))
|
||||||
|
|
||||||
|
assert exc.value.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_personal_upload_indexes_with_privileged_owner(tmp_path, monkeypatch):
|
||||||
|
monkeypatch.setenv("AUTH_ENABLED", "true")
|
||||||
|
monkeypatch.setattr(personal_routes, "UPLOADS_DIR", str(tmp_path))
|
||||||
|
rag = _FakeRAG()
|
||||||
|
monkeypatch.setattr(personal_routes, "get_rag_manager", lambda: rag)
|
||||||
|
|
||||||
|
endpoint = _upload_endpoint()
|
||||||
|
result = asyncio.run(
|
||||||
|
endpoint(
|
||||||
|
request=_request({"can_use_documents": True}),
|
||||||
|
files=[_Upload()],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["success"] is True
|
||||||
|
assert result["indexed_count"] == 1
|
||||||
|
assert rag.docs[0][0] == "hello from upload"
|
||||||
|
metadata = rag.docs[0][1]
|
||||||
|
assert metadata["owner"] == "alice"
|
||||||
|
assert Path(metadata["directory"]).name == "alice"
|
||||||
Reference in New Issue
Block a user