feat: add conditional bootstrap login + 23 admin login tests

- Add hash_password/verify_password to app/auth.py using bcrypt
- Fix /login to reject bootstrap creds (403) when admin users exist
- Add normal password-based login path for existing admin users
- Eagerly load User.roles to avoid lazy-load outside async session
- Fix test_user_models.py VARCHAR assertion (SQLAlchemy 2.0 uses 'string')
- Use shared-cache SQLite in conftest for endpoint-level tests
- Add 23 tests covering all 3 required scenarios plus edge cases:
  1. Bootstrap login with hardcoded creds when no admins → PASS (200)
  2. Bootstrap login with hardcoded creds when admins exist → FAIL (403)
  3. Password login for existing admin with hashed password → PASS (200)
- All 130 tests pass (107 existing + 23 new)
This commit is contained in:
Hermes Agent
2026-07-30 06:35:52 +00:00
parent 0eca079f28
commit f22802658e
5 changed files with 515 additions and 30 deletions
+14 -1
View File
@@ -7,13 +7,26 @@ import httpx
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from jose import JWTError, jwt
from sqlalchemy import select
from bcrypt import hashpw, checkpw, gensalt
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.config import settings
from app.database import get_session
from app.user_models import User
# ── Password hashing ───────────────────────────────────────────
def hash_password(password: str) -> str:
"""Hash a plaintext password using bcrypt."""
return hashpw(password.encode("utf-8"), gensalt()).decode("utf-8")
def verify_password(plain: str, hashed: str) -> bool:
"""Verify a plaintext password against a bcrypt hash."""
return bool(checkpw(plain.encode("utf-8"), hashed.encode("utf-8")))
class AuthBearer(HTTPBearer):
"""HTTP Bearer scheme that doesn't auto-fail on missing token."""