feat: add role-based admin support for multiple admin users
- Add Role model and user_roles association table (many-to-many) - Add password_hash column to User model (nullable, for future bcrypt) - Keep is_admin boolean for backward compatibility - Add is_admin_effective property: true if legacy is_admin OR 'admin' role - Update auth dependency (get_current_admin_user) to use is_admin_effective - Update bootstrap login to auto-create 'admin' role and assign on login - Update Google OAuth login to use is_admin_effective for token creation - Add migrate_roles.py: idempotent migration script to backfill roles - Add unit tests for Role, User, association table, and is_admin_effective
This commit is contained in:
+19
-4
@@ -57,8 +57,18 @@ async def login(payload: LoginRequest):
|
||||
|
||||
# This is handled in the auth router to keep it self-contained
|
||||
from app.database import async_session
|
||||
from app.user_models import Role
|
||||
|
||||
async with async_session() as session:
|
||||
# Ensure the 'admin' role exists
|
||||
result = await session.execute(select(Role).where(Role.name == "admin"))
|
||||
admin_role = result.scalar_one_or_none()
|
||||
if admin_role is None:
|
||||
admin_role = Role(name="admin", description="Full administrative access")
|
||||
session.add(admin_role)
|
||||
await session.commit()
|
||||
await session.refresh(admin_role)
|
||||
|
||||
# Find or create the bootstrap admin user
|
||||
result = await session.execute(
|
||||
select(User).where(
|
||||
@@ -74,11 +84,16 @@ async def login(payload: LoginRequest):
|
||||
auth_provider="local",
|
||||
is_admin=True,
|
||||
)
|
||||
user.roles.append(admin_role)
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
else:
|
||||
# Ensure existing bootstrap user also has the role
|
||||
if admin_role not in user.roles:
|
||||
user.roles.append(admin_role)
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
|
||||
token = create_access_token(user.id, user.is_admin)
|
||||
token = create_access_token(user.id, user.is_admin_effective)
|
||||
return TokenResponse(access_token=token)
|
||||
|
||||
|
||||
@@ -102,7 +117,7 @@ async def login_with_google(payload: GoogleLoginRequest, session: AsyncSession =
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
|
||||
token = create_access_token(user.id, user.is_admin)
|
||||
token = create_access_token(user.id, user.is_admin_effective)
|
||||
return TokenResponse(access_token=token)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user