jogosultsági szintek RBAC beállítva tesztelve
This commit is contained in:
385
backend/app/scripts/seed_rbac.py
Normal file
385
backend/app/scripts/seed_rbac.py
Normal file
@@ -0,0 +1,385 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/scripts/seed_rbac.py
|
||||
"""
|
||||
RBAC Phase 1: Seed script for database-driven Role-Based Access Control.
|
||||
|
||||
This script initializes the database with:
|
||||
- 6 system roles (SUPERADMIN, ADMIN, MODERATOR, SALES_REP, SERVICE_MGR, USER)
|
||||
- 28 permission codes across 8 domains
|
||||
- Role-permission mappings according to the audit proposal matrix
|
||||
- User migration: maps existing UserRole enum values to new role_id FK
|
||||
|
||||
Usage:
|
||||
docker exec sf_api python -m app.scripts.seed_rbac
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
from sqlalchemy import select, text, update
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.system.rbac import SystemRole, SystemPermission, SystemRolePermission
|
||||
from app.models.identity.identity import User, UserRole
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s]: %(message)s')
|
||||
logger = logging.getLogger("Seed-RBAC")
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# STEP A: 6 System Roles
|
||||
# ──────────────────────────────────────────────
|
||||
ROLES = [
|
||||
{"name": "SUPERADMIN", "description": "Platform super administrator with full system access", "rank": 100, "is_system": True},
|
||||
{"name": "ADMIN", "description": "Platform administrator with elevated management privileges", "rank": 80, "is_system": True},
|
||||
{"name": "MODERATOR", "description": "Content moderator with limited administrative access", "rank": 60, "is_system": True},
|
||||
{"name": "SALES_REP", "description": "Sales representative with fleet creation privileges", "rank": 40, "is_system": True},
|
||||
{"name": "SERVICE_MGR", "description": "Service manager with organization-level access", "rank": 20, "is_system": True},
|
||||
{"name": "USER", "description": "Regular platform user with basic access", "rank": 0, "is_system": True},
|
||||
]
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# STEP B: 28 Permission Codes
|
||||
# ──────────────────────────────────────────────
|
||||
PERMISSIONS = [
|
||||
# fleet domain (6 permissions)
|
||||
{"code": "fleet:view", "domain": "fleet", "action": "view", "description": "View fleet/vehicle data"},
|
||||
{"code": "fleet:create", "domain": "fleet", "action": "create", "description": "Create new fleet/vehicle entries"},
|
||||
{"code": "fleet:edit", "domain": "fleet", "action": "edit", "description": "Edit existing fleet/vehicle data"},
|
||||
{"code": "fleet:delete", "domain": "fleet", "action": "delete", "description": "Delete fleet/vehicle entries"},
|
||||
{"code": "fleet:approve", "domain": "fleet", "action": "approve","description": "Approve fleet/vehicle submissions"},
|
||||
|
||||
# user domain (5 permissions)
|
||||
{"code": "user:view", "domain": "user", "action": "view", "description": "View user profiles and data"},
|
||||
{"code": "user:create", "domain": "user", "action": "create", "description": "Create new user accounts"},
|
||||
{"code": "user:edit", "domain": "user", "action": "edit", "description": "Edit user profiles and data"},
|
||||
{"code": "user:delete", "domain": "user", "action": "delete", "description": "Delete user accounts"},
|
||||
{"code": "user:manage-roles", "domain": "user", "action": "manage-roles","description": "Manage user role assignments"},
|
||||
|
||||
# finance domain (4 permissions)
|
||||
{"code": "finance:view", "domain": "finance", "action": "view", "description": "View financial data and transactions"},
|
||||
{"code": "finance:edit", "domain": "finance", "action": "edit", "description": "Edit financial records"},
|
||||
{"code": "finance:approve", "domain": "finance", "action": "approve", "description": "Approve financial transactions"},
|
||||
{"code": "finance:refund", "domain": "finance", "action": "refund", "description": "Process refunds"},
|
||||
|
||||
# system domain (3 permissions)
|
||||
{"code": "system:view", "domain": "system", "action": "view", "description": "View system configuration"},
|
||||
{"code": "system:edit", "domain": "system", "action": "edit", "description": "Edit system configuration"},
|
||||
{"code": "system:manage", "domain": "system", "action": "manage", "description": "Full system management access"},
|
||||
|
||||
# org domain (4 permissions)
|
||||
{"code": "org:view", "domain": "org", "action": "view", "description": "View organization data"},
|
||||
{"code": "org:edit", "domain": "org", "action": "edit", "description": "Edit organization data"},
|
||||
{"code": "org:delete", "domain": "org", "action": "delete", "description": "Delete organizations"},
|
||||
{"code": "org:manage-members", "domain": "org", "action": "manage-members","description": "Manage organization members"},
|
||||
|
||||
# reports domain (3 permissions)
|
||||
{"code": "reports:view", "domain": "reports", "action": "view", "description": "View reports"},
|
||||
{"code": "reports:export", "domain": "reports", "action": "export", "description": "Export reports"},
|
||||
{"code": "reports:manage", "domain": "reports", "action": "manage", "description": "Manage report configurations"},
|
||||
|
||||
# audit domain (2 permissions)
|
||||
{"code": "audit:view", "domain": "audit", "action": "view", "description": "View audit logs"},
|
||||
{"code": "audit:export", "domain": "audit", "action": "export", "description": "Export audit logs"},
|
||||
|
||||
# settings domain (2 permissions)
|
||||
{"code": "settings:view", "domain": "settings", "action": "view", "description": "View system settings"},
|
||||
{"code": "settings:edit", "domain": "settings", "action": "edit", "description": "Edit system settings"},
|
||||
]
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# STEP C: Role-Permission Mapping Matrix
|
||||
# Format: { "role_name": {"permission_code": True/False} }
|
||||
# True = granted, False = denied (explicit)
|
||||
# ──────────────────────────────────────────────
|
||||
ROLE_PERMISSION_MATRIX = {
|
||||
"SUPERADMIN": {
|
||||
# fleet - all granted
|
||||
"fleet:view": True, "fleet:create": True, "fleet:edit": True,
|
||||
"fleet:delete": True, "fleet:approve": True,
|
||||
# user - all granted
|
||||
"user:view": True, "user:create": True, "user:edit": True,
|
||||
"user:delete": True, "user:manage-roles": True,
|
||||
# finance - all granted
|
||||
"finance:view": True, "finance:edit": True,
|
||||
"finance:approve": True, "finance:refund": True,
|
||||
# system - all granted
|
||||
"system:view": True, "system:edit": True, "system:manage": True,
|
||||
# org - all granted
|
||||
"org:view": True, "org:edit": True, "org:delete": True, "org:manage-members": True,
|
||||
# reports - all granted
|
||||
"reports:view": True, "reports:export": True, "reports:manage": True,
|
||||
# audit - all granted
|
||||
"audit:view": True, "audit:export": True,
|
||||
# settings - all granted
|
||||
"settings:view": True, "settings:edit": True,
|
||||
},
|
||||
"ADMIN": {
|
||||
# fleet
|
||||
"fleet:view": True, "fleet:create": True, "fleet:edit": True,
|
||||
"fleet:delete": False, "fleet:approve": True,
|
||||
# user
|
||||
"user:view": True, "user:create": True, "user:edit": True,
|
||||
"user:delete": False, "user:manage-roles": False,
|
||||
# finance
|
||||
"finance:view": True, "finance:edit": True,
|
||||
"finance:approve": False, "finance:refund": False,
|
||||
# system
|
||||
"system:view": True, "system:edit": True, "system:manage": False,
|
||||
# org
|
||||
"org:view": True, "org:edit": True, "org:delete": False, "org:manage-members": True,
|
||||
# reports
|
||||
"reports:view": True, "reports:export": True, "reports:manage": False,
|
||||
# audit
|
||||
"audit:view": False, "audit:export": False,
|
||||
# settings
|
||||
"settings:view": True, "settings:edit": True,
|
||||
},
|
||||
"MODERATOR": {
|
||||
# fleet
|
||||
"fleet:view": True, "fleet:create": False, "fleet:edit": False,
|
||||
"fleet:delete": False, "fleet:approve": False,
|
||||
# user
|
||||
"user:view": True, "user:create": False, "user:edit": False,
|
||||
"user:delete": False, "user:manage-roles": False,
|
||||
# finance
|
||||
"finance:view": True, "finance:edit": False,
|
||||
"finance:approve": False, "finance:refund": False,
|
||||
# system
|
||||
"system:view": True, "system:edit": False, "system:manage": False,
|
||||
# org
|
||||
"org:view": True, "org:edit": False, "org:delete": False, "org:manage-members": False,
|
||||
# reports
|
||||
"reports:view": True, "reports:export": True, "reports:manage": False,
|
||||
# audit
|
||||
"audit:view": False, "audit:export": False,
|
||||
# settings
|
||||
"settings:view": False, "settings:edit": False,
|
||||
},
|
||||
"SALES_REP": {
|
||||
# fleet
|
||||
"fleet:view": True, "fleet:create": True, "fleet:edit": False,
|
||||
"fleet:delete": False, "fleet:approve": False,
|
||||
# user
|
||||
"user:view": False, "user:create": False, "user:edit": False,
|
||||
"user:delete": False, "user:manage-roles": False,
|
||||
# finance
|
||||
"finance:view": False, "finance:edit": False,
|
||||
"finance:approve": False, "finance:refund": False,
|
||||
# system
|
||||
"system:view": False, "system:edit": False, "system:manage": False,
|
||||
# org
|
||||
"org:view": True, "org:edit": False, "org:delete": False, "org:manage-members": False,
|
||||
# reports
|
||||
"reports:view": True, "reports:export": False, "reports:manage": False,
|
||||
# audit
|
||||
"audit:view": False, "audit:export": False,
|
||||
# settings
|
||||
"settings:view": False, "settings:edit": False,
|
||||
},
|
||||
"SERVICE_MGR": {
|
||||
# fleet
|
||||
"fleet:view": True, "fleet:create": False, "fleet:edit": False,
|
||||
"fleet:delete": False, "fleet:approve": False,
|
||||
# user
|
||||
"user:view": False, "user:create": False, "user:edit": False,
|
||||
"user:delete": False, "user:manage-roles": False,
|
||||
# finance
|
||||
"finance:view": False, "finance:edit": False,
|
||||
"finance:approve": False, "finance:refund": False,
|
||||
# system
|
||||
"system:view": False, "system:edit": False, "system:manage": False,
|
||||
# org
|
||||
"org:view": True, "org:edit": False, "org:delete": False, "org:manage-members": False,
|
||||
# reports
|
||||
"reports:view": True, "reports:export": False, "reports:manage": False,
|
||||
# audit
|
||||
"audit:view": False, "audit:export": False,
|
||||
# settings
|
||||
"settings:view": False, "settings:edit": False,
|
||||
},
|
||||
"USER": {
|
||||
# fleet
|
||||
"fleet:view": True, "fleet:create": False, "fleet:edit": False,
|
||||
"fleet:delete": False, "fleet:approve": False,
|
||||
# user
|
||||
"user:view": False, "user:create": False, "user:edit": False,
|
||||
"user:delete": False, "user:manage-roles": False,
|
||||
# finance
|
||||
"finance:view": False, "finance:edit": False,
|
||||
"finance:approve": False, "finance:refund": False,
|
||||
# system
|
||||
"system:view": False, "system:edit": False, "system:manage": False,
|
||||
# org
|
||||
"org:view": False, "org:edit": False, "org:delete": False, "org:manage-members": False,
|
||||
# reports
|
||||
"reports:view": False, "reports:export": False, "reports:manage": False,
|
||||
# audit
|
||||
"audit:view": False, "audit:export": False,
|
||||
# settings
|
||||
"settings:view": False, "settings:edit": False,
|
||||
},
|
||||
}
|
||||
|
||||
# Map UserRole enum values to role names
|
||||
USERROLE_TO_ROLE_NAME = {
|
||||
UserRole.SUPERADMIN: "SUPERADMIN",
|
||||
UserRole.ADMIN: "ADMIN",
|
||||
UserRole.MODERATOR: "MODERATOR",
|
||||
UserRole.SALES_REP: "SALES_REP",
|
||||
UserRole.SERVICE_MGR: "SERVICE_MGR",
|
||||
UserRole.USER: "USER",
|
||||
}
|
||||
|
||||
|
||||
async def seed_rbac():
|
||||
"""Main seed function: creates roles, permissions, mappings, and migrates users."""
|
||||
async with AsyncSessionLocal() as db:
|
||||
try:
|
||||
# ── STEP A: Insert Roles ──
|
||||
logger.info("=" * 60)
|
||||
logger.info("STEP A: Inserting 6 system roles...")
|
||||
role_map = {} # name -> id
|
||||
for role_data in ROLES:
|
||||
existing = await db.execute(
|
||||
select(SystemRole).where(SystemRole.name == role_data["name"])
|
||||
)
|
||||
role = existing.scalar_one_or_none()
|
||||
if role:
|
||||
logger.info(f" ⏭️ Role '{role_data['name']}' already exists (id={role.id}), updating...")
|
||||
role.description = role_data["description"]
|
||||
role.rank = role_data["rank"]
|
||||
role.is_system = role_data["is_system"]
|
||||
else:
|
||||
role = SystemRole(**role_data)
|
||||
db.add(role)
|
||||
await db.flush()
|
||||
logger.info(f" ✅ Created role '{role_data['name']}' (id={role.id})")
|
||||
role_map[role_data["name"]] = role.id
|
||||
|
||||
await db.commit()
|
||||
logger.info(f" ✅ Roles committed. {len(role_map)} roles in role_map.")
|
||||
|
||||
# ── STEP B: Insert Permissions ──
|
||||
logger.info("=" * 60)
|
||||
logger.info("STEP B: Inserting 28 permission codes...")
|
||||
perm_map = {} # code -> id
|
||||
for perm_data in PERMISSIONS:
|
||||
existing = await db.execute(
|
||||
select(SystemPermission).where(SystemPermission.code == perm_data["code"])
|
||||
)
|
||||
perm = existing.scalar_one_or_none()
|
||||
if perm:
|
||||
logger.info(f" ⏭️ Permission '{perm_data['code']}' already exists (id={perm.id}), updating...")
|
||||
perm.domain = perm_data["domain"]
|
||||
perm.action = perm_data["action"]
|
||||
perm.description = perm_data["description"]
|
||||
else:
|
||||
perm = SystemPermission(**perm_data)
|
||||
db.add(perm)
|
||||
await db.flush()
|
||||
logger.info(f" ✅ Created permission '{perm_data['code']}' (id={perm.id})")
|
||||
perm_map[perm_data["code"]] = perm.id
|
||||
|
||||
await db.commit()
|
||||
logger.info(f" ✅ Permissions committed. {len(perm_map)} permissions in perm_map.")
|
||||
|
||||
# ── STEP C: Insert Role-Permission Mappings ──
|
||||
logger.info("=" * 60)
|
||||
logger.info("STEP C: Inserting role-permission mappings...")
|
||||
mapping_count = 0
|
||||
for role_name, perms in ROLE_PERMISSION_MATRIX.items():
|
||||
role_id = role_map.get(role_name)
|
||||
if not role_id:
|
||||
logger.warning(f" ⚠️ Role '{role_name}' not found in role_map, skipping!")
|
||||
continue
|
||||
|
||||
for perm_code, granted in perms.items():
|
||||
perm_id = perm_map.get(perm_code)
|
||||
if not perm_id:
|
||||
logger.warning(f" ⚠️ Permission '{perm_code}' not found in perm_map, skipping!")
|
||||
continue
|
||||
|
||||
# Check if mapping already exists
|
||||
existing = await db.execute(
|
||||
select(SystemRolePermission).where(
|
||||
SystemRolePermission.role_id == role_id,
|
||||
SystemRolePermission.permission_id == perm_id,
|
||||
)
|
||||
)
|
||||
rp = existing.scalar_one_or_none()
|
||||
if rp:
|
||||
if rp.granted != granted:
|
||||
rp.granted = granted
|
||||
logger.info(f" 🔄 Updated mapping: {role_name} -> {perm_code} (granted={granted})")
|
||||
else:
|
||||
rp = SystemRolePermission(
|
||||
role_id=role_id,
|
||||
permission_id=perm_id,
|
||||
granted=granted,
|
||||
)
|
||||
db.add(rp)
|
||||
mapping_count += 1
|
||||
|
||||
await db.commit()
|
||||
logger.info(f" ✅ {mapping_count} new role-permission mappings created.")
|
||||
|
||||
# ── STEP D: Migrate Existing Users ──
|
||||
logger.info("=" * 60)
|
||||
logger.info("STEP D: Migrating existing users to role_id...")
|
||||
|
||||
# Get all users that don't have role_id set yet
|
||||
result = await db.execute(
|
||||
select(User).where(User.role_id.is_(None))
|
||||
)
|
||||
users_to_migrate = result.scalars().all()
|
||||
|
||||
migrated_count = 0
|
||||
skipped_count = 0
|
||||
for user in users_to_migrate:
|
||||
role_name = USERROLE_TO_ROLE_NAME.get(user.role)
|
||||
if not role_name:
|
||||
logger.warning(f" ⚠️ User {user.id} has unknown role '{user.role}', skipping...")
|
||||
skipped_count += 1
|
||||
continue
|
||||
|
||||
role_id = role_map.get(role_name)
|
||||
if not role_id:
|
||||
logger.warning(f" ⚠️ Role '{role_name}' not found for user {user.id}, skipping...")
|
||||
skipped_count += 1
|
||||
continue
|
||||
|
||||
user.role_id = role_id
|
||||
migrated_count += 1
|
||||
|
||||
await db.commit()
|
||||
logger.info(f" ✅ Users migrated: {migrated_count} updated, {skipped_count} skipped.")
|
||||
|
||||
# ── FINAL REPORT ──
|
||||
logger.info("=" * 60)
|
||||
logger.info("📊 RBAC SEED SUMMARY")
|
||||
logger.info(f" Roles: {len(role_map)}")
|
||||
logger.info(f" Permissions: {len(perm_map)}")
|
||||
|
||||
# Count total role_permissions
|
||||
rp_result = await db.execute(select(SystemRolePermission))
|
||||
total_rp = len(rp_result.scalars().all())
|
||||
logger.info(f" Role-Permission Mappings: {total_rp}")
|
||||
|
||||
# Count users with role_id
|
||||
user_result = await db.execute(
|
||||
select(User).where(User.role_id.isnot(None))
|
||||
)
|
||||
users_with_role = len(user_result.scalars().all())
|
||||
logger.info(f" Users with role_id assigned: {users_with_role}")
|
||||
logger.info("=" * 60)
|
||||
logger.info("✅ RBAC Phase 1 seeding completed successfully!")
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"❌ RBAC seeding failed: {e}")
|
||||
raise
|
||||
|
||||
|
||||
async def main():
|
||||
logger.info("🚀 Starting RBAC Phase 1 seed...")
|
||||
await seed_rbac()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user