fix(librarian): apply tenant guard to explicit user args in client

LibraryDeskClient._resolve_user only enforced non-empty: an explicit
user argument to any tenant-scoped method bypassed tatlock's tenant
guard entirely and went straight to library-desk, and padded values
were sent un-stripped on the wire.

Route the explicit-arg path through the same apply_tenant_guard() used
by context resolution and strip whitespace before the empty check, so
a non-production environment can never send the production tenant (or
a sanitization-collision variant) to library-desk, regardless of how
the user was supplied. Defense in depth - no in-repo caller passes an
explicit user today.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 11:29:14 +02:00
co-authored by Claude Fable 5
parent e8e5d367b6
commit c00224222b
2 changed files with 62 additions and 4 deletions
+9 -4
View File
@@ -17,7 +17,7 @@ import httpx
from pydantic import BaseModel, Field
from src.core.config import config
from src.core.context import get_user
from src.core.context import apply_tenant_guard, get_user
from src.core.logging_config import get_logger
logger = get_logger(__name__)
@@ -271,14 +271,19 @@ class LibraryDeskClient:
request must carry an explicit tenant (a missing user will 422).
An empty tenant is a programming or configuration error - fail
loudly here, before any bytes hit the wire.
Explicit user arguments are stripped and routed through the same
tenant guard as context resolution (get_user() already applies
it), so a dev environment can never send the production tenant -
or a sanitization-collision variant of it - to library-desk.
"""
effective = user if user is not None else get_user()
if not effective or not effective.strip():
effective = (user if user is not None else get_user()).strip()
if not effective:
raise ValueError(
"library-desk request requires a non-empty user (tenant); "
"got an empty value from the caller or request context"
)
return effective
return apply_tenant_guard(effective)
async def _request_with_retry(
self,