ppt-web / tests /test_export_task_security.py
26fwyzpz6f-max
Clean deploy without binary files
6aecb2e
Raw
History Blame Contribute Delete
6.72 kB
from datetime import datetime
from pathlib import Path
from types import SimpleNamespace
from fastapi import FastAPI, HTTPException
from fastapi.testclient import TestClient
def _user(user_id: int, *, is_admin: bool = False):
return SimpleNamespace(id=user_id, is_admin=is_admin)
def _task(*, metadata=None, result=None, status="completed", task_type="pdf_generation"):
return SimpleNamespace(
task_id="task-1",
task_type=task_type,
status=SimpleNamespace(value=status),
progress=100.0,
created_at=datetime(2026, 4, 7, 12, 0, 0),
updated_at=datetime(2026, 4, 7, 12, 1, 0),
metadata=metadata or {},
result=result,
error=None,
)
def test_export_task_routes_require_authentication():
from landppt.web.route_modules import export_routes
app = FastAPI()
app.include_router(export_routes.router)
app.dependency_overrides[export_routes.get_current_user_required] = lambda: (_ for _ in ()).throw(
HTTPException(status_code=401, detail="Authentication required")
)
client = TestClient(app)
status_response = client.get("/api/landppt/tasks/task-1")
download_response = client.get("/api/landppt/tasks/task-1/download")
assert status_response.status_code == 401
assert download_response.status_code == 401
def test_export_task_status_hides_other_users_tasks(monkeypatch):
from landppt.services import background_tasks as background_tasks_module
from landppt.web.route_modules import export_routes
class FakeTaskManager:
async def get_task_async(self, task_id: str):
return _task(metadata={"project_id": "proj-1", "user_id": 22})
monkeypatch.setattr(background_tasks_module, "get_task_manager", lambda: FakeTaskManager())
app = FastAPI()
app.include_router(export_routes.router)
app.dependency_overrides[export_routes.get_current_user_required] = lambda: _user(11, is_admin=False)
client = TestClient(app)
response = client.get("/api/landppt/tasks/task-1")
assert response.status_code == 404
assert response.json()["detail"] == "Task not found"
def test_export_task_status_hides_ownerless_tasks_from_non_admin(monkeypatch):
from landppt.services import background_tasks as background_tasks_module
from landppt.web.route_modules import export_routes
class FakeTaskManager:
async def get_task_async(self, task_id: str):
return _task(metadata={"project_id": "proj-1"})
monkeypatch.setattr(background_tasks_module, "get_task_manager", lambda: FakeTaskManager())
app = FastAPI()
app.include_router(export_routes.router)
app.dependency_overrides[export_routes.get_current_user_required] = lambda: _user(11, is_admin=False)
client = TestClient(app)
response = client.get("/api/landppt/tasks/task-1")
assert response.status_code == 404
assert response.json()["detail"] == "Task not found"
def test_export_task_status_redacts_internal_paths(monkeypatch):
from landppt.services import background_tasks as background_tasks_module
from landppt.web.route_modules import export_routes
class FakeTaskManager:
async def get_task_async(self, task_id: str):
return _task(
metadata={
"project_id": "proj-1",
"project_topic": "Demo",
"user_id": 11,
"pdf_path": "/tmp/private.pdf",
"pptx_path": "/tmp/private.pptx",
"progress_message": "working",
},
result={
"success": True,
"pdf_path": "/tmp/private.pdf",
"pptx_path": "/tmp/private.pptx",
"project_topic": "Demo",
},
task_type="pdf_generation",
)
monkeypatch.setattr(background_tasks_module, "get_task_manager", lambda: FakeTaskManager())
app = FastAPI()
app.include_router(export_routes.router)
app.dependency_overrides[export_routes.get_current_user_required] = lambda: _user(11, is_admin=False)
client = TestClient(app)
response = client.get("/api/landppt/tasks/task-1")
assert response.status_code == 200
payload = response.json()
assert payload["message"] == "working"
assert payload["download_url"] == "/api/landppt/tasks/task-1/download"
assert payload["metadata"] == {
"project_id": "proj-1",
"project_topic": "Demo",
"user_id": 11,
"progress_message": "working",
}
assert payload["result"] == {
"success": True,
"project_topic": "Demo",
}
def test_export_task_download_allows_admin_bypass(monkeypatch, tmp_path):
from landppt.services import background_tasks as background_tasks_module
from landppt.web.route_modules import export_routes
pdf_path = tmp_path / "demo.pdf"
pdf_path.write_bytes(b"%PDF-1.4\n")
class FakeTaskManager:
async def get_task_async(self, task_id: str):
return _task(
metadata={"project_id": "proj-1", "project_topic": "Demo"},
result={"success": True, "pdf_path": str(pdf_path)},
task_type="pdf_generation",
)
monkeypatch.setattr(background_tasks_module, "get_task_manager", lambda: FakeTaskManager())
monkeypatch.setattr(background_tasks_module, "TaskStatus", SimpleNamespace(COMPLETED=_task().status))
app = FastAPI()
app.include_router(export_routes.router)
app.dependency_overrides[export_routes.get_current_user_required] = lambda: _user(1, is_admin=True)
client = TestClient(app)
response = client.get("/api/landppt/tasks/task-1/download")
assert response.status_code == 200
assert response.content.startswith(b"%PDF-1.4")
def test_export_task_route_source_includes_owner_scoping_changes():
source = Path("/root/clawd/src/landppt/web/route_modules/export_routes.py").read_text(encoding="utf-8")
assert 'metadata_filter={"project_id": project_id, "user_id": user.id}' in source
assert '"user_id": user.id' in source
assert '"metadata": _sanitize_task_mapping(task.metadata)' in source
assert 'response["result"] = _sanitize_task_mapping(task.result)' in source
assert "_ensure_task_access(task, user)" in source
def test_narration_task_route_source_includes_owner_scoping_changes():
source = Path("/root/clawd/src/landppt/web/route_modules/narration_routes.py").read_text(encoding="utf-8")
assert 'metadata_filter={"project_id": project_id, "language": language, "provider": provider, "user_id": user.id}' in source
assert '"user_id": user.id' in source