diff --git a/backend/app/api/auth.py b/backend/app/api/auth.py index 6a637d3..70a1f73 100644 --- a/backend/app/api/auth.py +++ b/backend/app/api/auth.py @@ -14,10 +14,32 @@ from app.models.user import User router = APIRouter() +async def _config_admin(db: AsyncSession): + """The admin row that holds instance-wide PocketID settings. + + Settings live on an admin user row, but there can be more than one admin. + Prefer the admin that actually has an issuer configured; otherwise fall back + to the lowest-id admin. Without this, an unordered LIMIT 1 could return an + admin with no config and make PocketID look disabled / gating inconsistent. + """ + result = await db.execute( + select(User) + .where(User.is_admin == True, User.pocketid_issuer.isnot(None)) + .order_by(User.id) + .limit(1) + ) + admin = result.scalar_one_or_none() + if admin is None: + result = await db.execute( + select(User).where(User.is_admin == True).order_by(User.id).limit(1) + ) + admin = result.scalar_one_or_none() + return admin + + async def _get_pocketid_config(db: AsyncSession): """Get PocketID config from DB (admin user) falling back to env vars.""" - result = await db.execute(select(User).where(User.is_admin == True).limit(1)) - admin = result.scalar_one_or_none() + admin = await _config_admin(db) issuer = (admin and admin.pocketid_issuer) or settings.pocketid_issuer client_id = (admin and admin.pocketid_client_id) or settings.pocketid_client_id client_secret = (admin and admin.pocketid_client_secret) or settings.pocketid_client_secret @@ -26,8 +48,7 @@ async def _get_pocketid_config(db: AsyncSession): async def _get_allowed_group(db: AsyncSession): """Group a PocketID user must belong to in order to sign in (None = allow all).""" - result = await db.execute(select(User).where(User.is_admin == True).limit(1)) - admin = result.scalar_one_or_none() + admin = await _config_admin(db) group = (admin and admin.pocketid_allowed_group) or settings.pocketid_allowed_group return (group or "").strip() or None diff --git a/milevault_export/backend/app/api/auth.py b/milevault_export/backend/app/api/auth.py index 6a637d3..70a1f73 100644 --- a/milevault_export/backend/app/api/auth.py +++ b/milevault_export/backend/app/api/auth.py @@ -14,10 +14,32 @@ from app.models.user import User router = APIRouter() +async def _config_admin(db: AsyncSession): + """The admin row that holds instance-wide PocketID settings. + + Settings live on an admin user row, but there can be more than one admin. + Prefer the admin that actually has an issuer configured; otherwise fall back + to the lowest-id admin. Without this, an unordered LIMIT 1 could return an + admin with no config and make PocketID look disabled / gating inconsistent. + """ + result = await db.execute( + select(User) + .where(User.is_admin == True, User.pocketid_issuer.isnot(None)) + .order_by(User.id) + .limit(1) + ) + admin = result.scalar_one_or_none() + if admin is None: + result = await db.execute( + select(User).where(User.is_admin == True).order_by(User.id).limit(1) + ) + admin = result.scalar_one_or_none() + return admin + + async def _get_pocketid_config(db: AsyncSession): """Get PocketID config from DB (admin user) falling back to env vars.""" - result = await db.execute(select(User).where(User.is_admin == True).limit(1)) - admin = result.scalar_one_or_none() + admin = await _config_admin(db) issuer = (admin and admin.pocketid_issuer) or settings.pocketid_issuer client_id = (admin and admin.pocketid_client_id) or settings.pocketid_client_id client_secret = (admin and admin.pocketid_client_secret) or settings.pocketid_client_secret @@ -26,8 +48,7 @@ async def _get_pocketid_config(db: AsyncSession): async def _get_allowed_group(db: AsyncSession): """Group a PocketID user must belong to in order to sign in (None = allow all).""" - result = await db.execute(select(User).where(User.is_admin == True).limit(1)) - admin = result.scalar_one_or_none() + admin = await _config_admin(db) group = (admin and admin.pocketid_allowed_group) or settings.pocketid_allowed_group return (group or "").strip() or None