feat!: require explicit user on every tenant-data endpoint

Remove the implicit jpmschweitzer default tenant (DEFAULT_USER) from
src/core/multi_tenancy.py and every endpoint and request model that
inherited it (~40 endpoints across /query, /wiki, /vector, /graph,
/ingest, /volatile, /documents, /stats, /rag).

- Add validate_required_user() + RequiredUser pydantic type in
  multi_tenancy and a shared require_user FastAPI dependency
  (RequiredUserQuery) that rejects missing, empty, and whitespace-only
  users with 422, following the /maintenance/* pattern.
- Wiki page create / smart-create / dossier request models now require
  user (no fallback in wiki_service).
- /maintenance/cleanup/test-data derives the tenant from the page path
  instead of using the production tenant collection.
- Wiki.js change listener skips changes when no tenant user can be
  derived from the notification email instead of defaulting to the
  production tenant.
- Consolidation service internal helpers no longer default to the
  production tenant.
- Tool catalog marks user as required with honest descriptions.
- OpenAPI descriptions updated honestly; CHANGELOG notes that callers
  (tatlock, Scheduler ingest tasks) must now send explicit user.
- Offline tests: 422 coverage for query/body endpoints, required-user
  validator tests; updated legacy tests that assumed a default tenant.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 11:06:11 +02:00
co-authored by Claude Fable 5
parent a66d801abd
commit 84e9185371
27 changed files with 481 additions and 153 deletions
+8 -7
View File
@@ -18,8 +18,9 @@ from src.services.vector_service import VectorService
from src.clients.qdrant_client import QdrantClientWrapper
from src.clients.wikijs_client import WikiJSClient
from src.clients.ollama_client import OllamaClient
from src.core.dependencies import QdrantDep, WikiJSDep, OllamaDep, verify_api_key
from src.core.multi_tenancy import DEFAULT_USER
from src.core.dependencies import (
QdrantDep, WikiJSDep, OllamaDep, verify_api_key, RequiredUserQuery
)
logger = logging.getLogger(__name__)
@@ -52,7 +53,7 @@ async def semantic_search(
```json
{
"query": "how to configure docker",
"user": "jpmschweitzer",
"user": "<tenant>",
"limit": 10,
"score_threshold": 0.5
}
@@ -77,7 +78,7 @@ async def semantic_search(
@router.post("/update-from-page/{page_id}", response_model=VectorUpdateSummary)
async def update_vectors_from_page(
page_id: int,
user: str = Query(default=DEFAULT_USER, description="User identifier"),
user: RequiredUserQuery,
force_refresh: bool = Query(default=False, description="Force re-embedding"),
vector_service: VectorService = Depends(get_vector_service),
api_key: str = Depends(verify_api_key)
@@ -96,7 +97,7 @@ async def update_vectors_from_page(
- Called manually by user/Librarian to refresh vectors
- Called by Scheduler for batch processing
**Example:** `POST /vector/update-from-page/5?user=jpmschweitzer`
**Example:** `POST /vector/update-from-page/5?user=<tenant> (user is REQUIRED)`
**Returns:** Summary with chunks created and processing time
"""
@@ -125,7 +126,7 @@ async def update_vectors_from_page(
@router.delete("/pages/{page_id}", response_model=DeletePageChunksResponse)
async def delete_page_chunks(
page_id: int,
user: str = Query(default=DEFAULT_USER, description="User identifier"),
user: RequiredUserQuery,
vector_service: VectorService = Depends(get_vector_service),
api_key: str = Depends(verify_api_key)
):
@@ -134,7 +135,7 @@ async def delete_page_chunks(
This is automatically called when a page is deleted from the wiki.
**Example:** `DELETE /vector/pages/5?user=jpmschweitzer`
**Example:** `DELETE /vector/pages/5?user=<tenant> (user is REQUIRED)`
"""
try:
deleted_count = await vector_service.delete_page_chunks(