feat(core): enforce tenant isolation guard outside production

Non-production environments (development/testing) now force the
effective tenant to the reserved test tenant "llm_tester" (or a
test_-prefixed override) regardless of DEFAULT_USER misconfiguration:

- Config.effective_default_user only honors DEFAULT_USER outside
  production when it is llm_tester or test_-prefixed; anything else
  is forced to llm_tester (tenant_forced flags the override)
- Config refuses startup (validation error) when a non-production
  environment is explicitly configured with the production tenant
  jpmschweitzer
- get_user() applies the same guard at request-context resolution,
  so an explicit request for the production tenant in dev/test is
  forced to llm_tester with a warning log
- initialize_application() emits one loud startup log line
  (tenant_guard_active / tenant_guard_production) stating the
  effective tenant

Unit tests cover the dev/test/prod x default/explicit-user matrix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 10:56:29 +02:00
co-authored by Claude Fable 5
parent 7ce1c1a314
commit 4b786a766c
5 changed files with 316 additions and 9 deletions
+30 -1
View File
@@ -41,6 +41,33 @@ current_conversation: ContextVar[str | None] = ContextVar(
)
def _apply_tenant_guard(user: str) -> str:
"""
Enforce tenant isolation at request-context resolution.
In non-production environments the production tenant must never be
the effective user - a request that explicitly asks for it is forced
to the reserved test tenant instead (with a loud log line).
"""
# Import here to avoid circular dependency
from src.core.config import PRODUCTION_TENANT, TEST_TENANT, Environment, config
if (
config.ENVIRONMENT != Environment.PRODUCTION
and user == PRODUCTION_TENANT
):
from src.core.logging_config import get_logger
get_logger(__name__).warning(
"tenant_guard_forced",
environment=config.ENVIRONMENT.value,
requested_tenant=user,
forced_tenant=TEST_TENANT,
)
return TEST_TENANT
return user
def get_user() -> str:
"""
Get current user from request context.
@@ -48,6 +75,8 @@ def get_user() -> str:
Returns:
User identifier for the current request.
Falls back to environment-aware default if not set.
In non-production environments the production tenant is never
returned - the tenant guard forces the reserved test tenant.
Example:
user = get_user() # "llm_tester" (dev) or "jpmschweitzer" (prod)
@@ -55,7 +84,7 @@ def get_user() -> str:
user = current_user.get()
if user == _USER_NOT_SET:
return get_default_user()
return user
return _apply_tenant_guard(user)
def get_conversation_id() -> str | None: