155 lines
5.7 KiB
Python
155 lines
5.7 KiB
Python
# /opt/docker/dev/service_finder/backend/app/scripts/migrate_rbac_phase1_enums.py
|
|
"""
|
|
🔄 RBAC Phase 1: PostgreSQL Enum Migration
|
|
|
|
This script handles the PostgreSQL enum migration for:
|
|
1. identity.userrole -> new values: SUPERADMIN, ADMIN, MODERATOR, SALES_REP, SERVICE_MGR, USER
|
|
2. fleet.org_roles table seeding (delegated to seed_org_roles.py)
|
|
|
|
Since PostgreSQL doesn't support ALTER TYPE ... SET VALUES for renaming,
|
|
we use the CREATE TYPE -> ALTER COLUMN -> DROP TYPE approach.
|
|
|
|
Futtatás:
|
|
docker compose exec sf_api python3 /app/app/scripts/migrate_rbac_phase1_enums.py
|
|
"""
|
|
|
|
import asyncio
|
|
import logging
|
|
from app.database import AsyncSessionLocal
|
|
from sqlalchemy import text
|
|
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s]: %(message)s')
|
|
logger = logging.getLogger("Migrate-RBAC-Enums")
|
|
|
|
|
|
async def migrate_userrole_enum():
|
|
"""Migrate identity.userrole enum to new values.
|
|
|
|
Old values: superadmin, admin, moderator, sales_agent, user, service_owner, fleet_manager, driver
|
|
New values: SUPERADMIN, ADMIN, MODERATOR, SALES_REP, SERVICE_MGR, USER
|
|
|
|
Mapping:
|
|
superadmin -> SUPERADMIN
|
|
admin -> ADMIN
|
|
moderator -> MODERATOR
|
|
sales_agent -> SALES_REP
|
|
user -> USER
|
|
service_owner -> USER (fallback)
|
|
fleet_manager -> USER (fallback)
|
|
driver -> USER (fallback)
|
|
"""
|
|
async with AsyncSessionLocal() as session:
|
|
logger.info("🔍 Checking current identity.userrole enum...")
|
|
|
|
# Check current enum values
|
|
result = await session.execute(
|
|
text("SELECT unnest(enum_range(NULL::identity.userrole))::text as val")
|
|
)
|
|
current_values = {row[0] for row in result.fetchall()}
|
|
logger.info(f"Current enum values: {current_values}")
|
|
|
|
new_values = {"SUPERADMIN", "ADMIN", "MODERATOR", "SALES_REP", "SERVICE_MGR", "USER"}
|
|
|
|
# If already migrated, skip
|
|
if new_values.issubset(current_values):
|
|
logger.info("✅ Enum already has the new values. Skipping migration.")
|
|
return
|
|
|
|
logger.info("🔄 Starting enum migration...")
|
|
|
|
# Step 1: Create new enum type
|
|
logger.info("Step 1: Creating new enum type 'userrole_new'...")
|
|
await session.execute(text("""
|
|
CREATE TYPE identity.userrole_new AS ENUM (
|
|
'SUPERADMIN', 'ADMIN', 'MODERATOR', 'SALES_REP', 'SERVICE_MGR', 'USER'
|
|
)
|
|
"""))
|
|
|
|
# Step 2: Alter the users table to use text temporarily
|
|
logger.info("Step 2: Altering column to text type...")
|
|
await session.execute(text("""
|
|
ALTER TABLE identity.users
|
|
ALTER COLUMN role TYPE text
|
|
USING role::text
|
|
"""))
|
|
|
|
# Step 3: Update old values to new values
|
|
logger.info("Step 3: Updating role values...")
|
|
await session.execute(text("""
|
|
UPDATE identity.users SET role = 'SUPERADMIN' WHERE role = 'superadmin'
|
|
"""))
|
|
await session.execute(text("""
|
|
UPDATE identity.users SET role = 'ADMIN' WHERE role = 'admin'
|
|
"""))
|
|
await session.execute(text("""
|
|
UPDATE identity.users SET role = 'MODERATOR' WHERE role = 'moderator'
|
|
"""))
|
|
await session.execute(text("""
|
|
UPDATE identity.users SET role = 'SALES_REP' WHERE role = 'sales_agent'
|
|
"""))
|
|
await session.execute(text("""
|
|
UPDATE identity.users SET role = 'USER' WHERE role IN ('user', 'service_owner', 'fleet_manager', 'driver')
|
|
"""))
|
|
|
|
# Step 4: Drop default before altering type
|
|
logger.info("Step 4: Dropping default value...")
|
|
await session.execute(text("""
|
|
ALTER TABLE identity.users
|
|
ALTER COLUMN role DROP DEFAULT
|
|
"""))
|
|
|
|
# Step 5: Alter column to use new enum
|
|
logger.info("Step 5: Altering column to new enum type...")
|
|
await session.execute(text("""
|
|
ALTER TABLE identity.users
|
|
ALTER COLUMN role TYPE identity.userrole_new
|
|
USING role::text::identity.userrole_new
|
|
"""))
|
|
|
|
# Step 6: Set default
|
|
logger.info("Step 6: Setting default value...")
|
|
await session.execute(text("""
|
|
ALTER TABLE identity.users
|
|
ALTER COLUMN role SET DEFAULT 'USER'::identity.userrole_new
|
|
"""))
|
|
|
|
# Step 7: Drop old enum and rename new one
|
|
logger.info("Step 7: Dropping old enum and renaming new one...")
|
|
await session.execute(text("DROP TYPE IF EXISTS identity.userrole"))
|
|
await session.execute(text("""
|
|
ALTER TYPE identity.userrole_new RENAME TO userrole
|
|
"""))
|
|
|
|
await session.commit()
|
|
logger.info("✅ Enum migration completed successfully!")
|
|
|
|
# Verify
|
|
result = await session.execute(
|
|
text("SELECT unnest(enum_range(NULL::identity.userrole))::text as val")
|
|
)
|
|
final_values = [row[0] for row in result.fetchall()]
|
|
logger.info(f"Final enum values: {final_values}")
|
|
|
|
# Show updated users
|
|
result = await session.execute(
|
|
text("SELECT id, email, role::text FROM identity.users ORDER BY id")
|
|
)
|
|
users = result.fetchall()
|
|
logger.info(f"\nUpdated {len(users)} users:")
|
|
for row in users:
|
|
logger.info(f" id={row[0]:4d} | email={row[1]:30s} | role={row[2]}")
|
|
|
|
|
|
async def main():
|
|
logger.info("=" * 60)
|
|
logger.info("🔄 RBAC Phase 1: PostgreSQL Enum Migration")
|
|
logger.info("=" * 60)
|
|
await migrate_userrole_enum()
|
|
logger.info("=" * 60)
|
|
logger.info("✅ Migration completed.")
|
|
logger.info("=" * 60)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|