RABC fejlesztése és beélesítése hibák kijavításával
This commit is contained in:
154
backend/app/scripts/migrate_rbac_phase1_enums.py
Normal file
154
backend/app/scripts/migrate_rbac_phase1_enums.py
Normal file
@@ -0,0 +1,154 @@
|
||||
# /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())
|
||||
Reference in New Issue
Block a user