diff --git a/CHANGELOG.md b/CHANGELOG.md index a19da68..cd3418e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed `TypeError: can't subtract offset-naive and offset-aware datetimes` in `process_mail_account` task when computing `duration_seconds`. After a database refresh, `started_at` may be returned as a naive datetime; it is now normalized to UTC before subtraction. +- **Admin user not seeing admin dashboard**: Added startup auto-promotion in `main.py` lifespan handler — on every application start, if the user matching `ADMIN_EMAIL` exists in the database but does not yet have `is_superuser=True`, they are promoted immediately. This fixes accounts created before the auto-promotion-on-login code was deployed (e.g. `christianlouis@gmail.com` was logged in but saw no admin section). ### Security - Upgraded `python-jose` from 3.3.0 to 3.5.0 to fix CVE: algorithm confusion vulnerability with OpenSSH ECDSA keys (affected versions < 3.4.0). diff --git a/backend/app/main.py b/backend/app/main.py index ed40431..cd0e036 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -58,6 +58,34 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]: except Exception as exc: logger.warning("Could not seed default settings: %s", exc, exc_info=True) + # Ensure the configured ADMIN_EMAIL user has is_superuser=True. + # This runs on every startup so that existing accounts created before the + # auto-promotion login logic existed are also promoted correctly. + if settings.ADMIN_EMAIL: + try: + from sqlalchemy import select, func + from app.core.database import async_session_maker + from app.models.database_models import User + + async with async_session_maker() as db: + result = await db.execute( + select(User).where( + func.lower(User.email) == settings.ADMIN_EMAIL.lower() + ) + ) + admin_user = result.scalar_one_or_none() + if admin_user and not admin_user.is_superuser: + admin_user.is_superuser = True # type: ignore[assignment] + await db.commit() + logger.info( + "Auto-promoted admin user to superuser on startup: %s", + admin_user.email, + ) + except Exception as exc: + logger.warning( + "Could not auto-promote admin user on startup: %s", exc, exc_info=True + ) + yield # Shutdown logger.info("Shutting down application") diff --git a/docs/TODO.md b/docs/TODO.md index 87e711b..d13dc6d 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -245,7 +245,7 @@ because the API client layer is missing. - [x] Admin overview page (`/admin`) with system-wide stats - [x] User management page (`/admin/users`) — list, edit, delete users; assign plans; promote/demote admin - [x] Plan management page (`/admin/plans`) — full CRUD for subscription plans (mailboxes, emails/day, interval, pricing) -- [x] `ADMIN_EMAIL` env var with default `christianlouis@gmail.com`; admin auto-promoted on login +- [x] `ADMIN_EMAIL` env var with default `christianlouis@gmail.com`; admin auto-promoted on login and on every application startup (fixes pre-existing accounts) - [x] `is_superuser` exposed in `/users/me` response - [x] Admin badge (purple shield) shown in top bar for superusers