Phase 3: Fix medium-priority issues (M1-M4, M6)

M1: Password complexity validation (min 8 chars, uppercase, lowercase, digit)
M2: Remove is_system_admin from login response (prevent role leaking)
M3: Permission cache invalidates on DB error instead of using stale data
M4: .env.docker.example already fixed in B9 (SECRET_KEY, FRONTEND_URL, SMTP)
M6: Frontend test setup auto-wraps with QueryClientProvider (fixes ~29 test failures)

Remaining: M5 (frontend component integration — WelcomeDialog, SavedFilterBar, etc.)
This commit is contained in:
Agent Zero
2026-07-26 20:51:40 +02:00
parent 604a2b7648
commit 825d638130
5 changed files with 68 additions and 5 deletions
+4 -2
View File
@@ -337,11 +337,13 @@ async def get_cached_permissions(
except Exception:
logger.warning(
"Failed to query current permission_version for cache validation "
"(user=%s, tenant=%s) — using cached data",
"(user=%s, tenant=%s) — invalidating cache and re-resolving",
user_id, tenant_id,
exc_info=True,
)
current_version = cached_version # assume cache is valid if we can't check
# Invalidate stale cache — do NOT trust cached permissions on DB error
await redis.delete(cache_key)
return None # Fall through to re-resolution from DB
if cached_version == current_version:
return data
-1
View File
@@ -75,7 +75,6 @@ async def login(
"email": user.email,
"name": user.name,
"role": role,
"is_system_admin": user.is_system_admin,
"tenant_id": str(tenant.id),
"tenant_name": tenant.name,
"csrf_token": csrf_token,
+7 -1
View File
@@ -2,7 +2,8 @@
from __future__ import annotations
from pydantic import BaseModel, EmailStr, Field
from pydantic import BaseModel, EmailStr, Field, field_validator
from app.schemas.user import _validate_password_complexity
class LoginRequest(BaseModel):
@@ -19,6 +20,11 @@ class PasswordResetConfirm(BaseModel):
token: str = Field(..., min_length=1)
new_password: str = Field(..., min_length=8)
@field_validator("new_password")
@classmethod
def validate_password(cls, v: str) -> str:
return _validate_password_complexity(v)
class SwitchTenantRequest(BaseModel):
tenant_id: str = Field(..., min_length=1)
+23 -1
View File
@@ -2,7 +2,24 @@
from __future__ import annotations
from pydantic import BaseModel, EmailStr, Field
import re
from pydantic import BaseModel, EmailStr, Field, field_validator
def _validate_password_complexity(password: str) -> str:
"""Validate password meets complexity requirements.
Requires: min 8 chars, at least 1 uppercase, 1 lowercase, 1 digit.
"""
if len(password) < 8:
raise ValueError("Password must be at least 8 characters")
if not re.search(r"[A-Z]", password):
raise ValueError("Password must contain at least one uppercase letter")
if not re.search(r"[a-z]", password):
raise ValueError("Password must contain at least one lowercase letter")
if not re.search(r"\d", password):
raise ValueError("Password must contain at least one digit")
return password
class UserCreate(BaseModel):
@@ -13,6 +30,11 @@ class UserCreate(BaseModel):
role_id: str | None = Field(default=None, description="UUID of a custom Role", examples=["550e8400-e29b-41d4-a716-446655440000"])
is_active: bool = True
@field_validator("password")
@classmethod
def validate_password(cls, v: str) -> str:
return _validate_password_complexity(v)
class UserUpdate(BaseModel):
name: str | None = Field(None, min_length=1, max_length=200)