Files
membership-be/seed_permissions_rbac.py
2025-12-17 01:41:29 +07:00

285 lines
15 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Permission Seeding Script for Dynamic RBAC System
This script populates the database with 56 granular permissions and assigns them
to the appropriate dynamic roles (not the old enum roles).
Usage:
python3 seed_permissions_rbac.py
Environment Variables:
DATABASE_URL - PostgreSQL connection string
"""
import os
import sys
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from database import Base
from models import Permission, RolePermission, Role
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Database connection
DATABASE_URL = os.getenv("DATABASE_URL")
if not DATABASE_URL:
print("Error: DATABASE_URL environment variable not set")
sys.exit(1)
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# ============================================================
# Permission Definitions (56 permissions across 9 modules)
# ============================================================
PERMISSIONS = [
# ========== USERS MODULE (10) ==========
{"code": "users.view", "name": "View Users", "description": "View user list and user profiles", "module": "users"},
{"code": "users.create", "name": "Create Users", "description": "Create new users and send invitations", "module": "users"},
{"code": "users.edit", "name": "Edit Users", "description": "Edit user profiles and information", "module": "users"},
{"code": "users.delete", "name": "Delete Users", "description": "Delete user accounts", "module": "users"},
{"code": "users.status", "name": "Change User Status", "description": "Change user status (active, inactive, etc.)", "module": "users"},
{"code": "users.approve", "name": "Approve/Validate Users", "description": "Approve or validate user applications", "module": "users"},
{"code": "users.export", "name": "Export Users", "description": "Export user data to CSV", "module": "users"},
{"code": "users.import", "name": "Import Users", "description": "Import users from CSV", "module": "users"},
{"code": "users.reset_password", "name": "Reset User Password", "description": "Reset user passwords via email", "module": "users"},
{"code": "users.resend_verification", "name": "Resend Verification Email", "description": "Resend email verification links", "module": "users"},
{"code": "users.invite", "name": "Invite Users", "description": "Send user invitations", "module": "users"},
# ========== EVENTS MODULE (8) ==========
{"code": "events.view", "name": "View Events", "description": "View event list and event details", "module": "events"},
{"code": "events.create", "name": "Create Events", "description": "Create new events", "module": "events"},
{"code": "events.edit", "name": "Edit Events", "description": "Edit existing events", "module": "events"},
{"code": "events.delete", "name": "Delete Events", "description": "Delete events", "module": "events"},
{"code": "events.publish", "name": "Publish Events", "description": "Publish or unpublish events", "module": "events"},
{"code": "events.attendance", "name": "Mark Event Attendance", "description": "Mark user attendance for events", "module": "events"},
{"code": "events.rsvps", "name": "View Event RSVPs", "description": "View and manage event RSVPs", "module": "events"},
{"code": "events.calendar_export", "name": "Export Event Calendar", "description": "Export events to iCal format", "module": "events"},
# ========== SUBSCRIPTIONS MODULE (6) ==========
{"code": "subscriptions.view", "name": "View Subscriptions", "description": "View subscription list and details", "module": "subscriptions"},
{"code": "subscriptions.create", "name": "Create Subscriptions", "description": "Create manual subscriptions for users", "module": "subscriptions"},
{"code": "subscriptions.edit", "name": "Edit Subscriptions", "description": "Edit subscription details", "module": "subscriptions"},
{"code": "subscriptions.cancel", "name": "Cancel Subscriptions", "description": "Cancel user subscriptions", "module": "subscriptions"},
{"code": "subscriptions.activate", "name": "Activate Subscriptions", "description": "Manually activate subscriptions", "module": "subscriptions"},
{"code": "subscriptions.plans", "name": "Manage Subscription Plans", "description": "Create and edit subscription plans", "module": "subscriptions"},
# ========== FINANCIALS MODULE (6) ==========
{"code": "financials.view", "name": "View Financial Reports", "description": "View financial reports and dashboards", "module": "financials"},
{"code": "financials.create", "name": "Create Financial Reports", "description": "Upload and create financial reports", "module": "financials"},
{"code": "financials.edit", "name": "Edit Financial Reports", "description": "Edit existing financial reports", "module": "financials"},
{"code": "financials.delete", "name": "Delete Financial Reports", "description": "Delete financial reports", "module": "financials"},
{"code": "financials.export", "name": "Export Financial Data", "description": "Export financial data to CSV/PDF", "module": "financials"},
{"code": "financials.payments", "name": "View Payment Details", "description": "View detailed payment information", "module": "financials"},
# ========== NEWSLETTERS MODULE (6) ==========
{"code": "newsletters.view", "name": "View Newsletters", "description": "View newsletter archives", "module": "newsletters"},
{"code": "newsletters.create", "name": "Create Newsletters", "description": "Upload and create newsletters", "module": "newsletters"},
{"code": "newsletters.edit", "name": "Edit Newsletters", "description": "Edit existing newsletters", "module": "newsletters"},
{"code": "newsletters.delete", "name": "Delete Newsletters", "description": "Delete newsletter archives", "module": "newsletters"},
{"code": "newsletters.send", "name": "Send Newsletters", "description": "Send newsletter emails to subscribers", "module": "newsletters"},
{"code": "newsletters.subscribers", "name": "Manage Newsletter Subscribers", "description": "View and manage newsletter subscribers", "module": "newsletters"},
# ========== BYLAWS MODULE (5) ==========
{"code": "bylaws.view", "name": "View Bylaws", "description": "View organization bylaws documents", "module": "bylaws"},
{"code": "bylaws.create", "name": "Create Bylaws", "description": "Upload new bylaws documents", "module": "bylaws"},
{"code": "bylaws.edit", "name": "Edit Bylaws", "description": "Edit existing bylaws documents", "module": "bylaws"},
{"code": "bylaws.delete", "name": "Delete Bylaws", "description": "Delete bylaws documents", "module": "bylaws"},
{"code": "bylaws.publish", "name": "Publish Bylaws", "description": "Mark bylaws as current/published version", "module": "bylaws"},
# ========== GALLERY MODULE (5) ==========
{"code": "gallery.view", "name": "View Event Gallery", "description": "View event gallery photos", "module": "gallery"},
{"code": "gallery.upload", "name": "Upload Photos", "description": "Upload photos to event galleries", "module": "gallery"},
{"code": "gallery.edit", "name": "Edit Photos", "description": "Edit photo captions and details", "module": "gallery"},
{"code": "gallery.delete", "name": "Delete Photos", "description": "Delete photos from galleries", "module": "gallery"},
{"code": "gallery.moderate", "name": "Moderate Gallery Content", "description": "Approve/reject uploaded photos", "module": "gallery"},
# ========== SETTINGS MODULE (6) ==========
{"code": "settings.view", "name": "View Settings", "description": "View application settings", "module": "settings"},
{"code": "settings.edit", "name": "Edit Settings", "description": "Edit application settings", "module": "settings"},
{"code": "settings.email_templates", "name": "Manage Email Templates", "description": "Edit email templates and notifications", "module": "settings"},
{"code": "settings.storage", "name": "Manage Storage", "description": "View and manage storage usage", "module": "settings"},
{"code": "settings.backup", "name": "Backup & Restore", "description": "Create and restore database backups", "module": "settings"},
{"code": "settings.logs", "name": "View System Logs", "description": "View application and audit logs", "module": "settings"},
# ========== PERMISSIONS MODULE (4) ==========
{"code": "permissions.view", "name": "View Permissions", "description": "View permission definitions and assignments", "module": "permissions"},
{"code": "permissions.assign", "name": "Assign Permissions", "description": "Assign permissions to roles", "module": "permissions"},
{"code": "permissions.manage_roles", "name": "Manage Roles", "description": "Create and manage user roles", "module": "permissions"},
{"code": "permissions.audit", "name": "View Permission Audit Log", "description": "View permission change audit logs", "module": "permissions"},
]
# Default permission assignments for dynamic roles
DEFAULT_ROLE_PERMISSIONS = {
"guest": [], # Guests have no permissions
"member": [
# Members can view public content
"events.view", "events.rsvps", "events.calendar_export",
"newsletters.view", "bylaws.view", "gallery.view",
],
"finance": [
# Finance role has financial permissions + some user viewing
"users.view", "financials.view", "financials.create", "financials.edit",
"financials.delete", "financials.export", "financials.payments",
"subscriptions.view", "subscriptions.create", "subscriptions.edit",
"subscriptions.cancel", "subscriptions.activate", "subscriptions.plans",
],
"admin": [
# Admins have most permissions except RBAC management
"users.view", "users.create", "users.edit", "users.status", "users.approve",
"users.export", "users.import", "users.reset_password", "users.resend_verification",
"users.invite",
"events.view", "events.create", "events.edit", "events.delete", "events.publish",
"events.attendance", "events.rsvps", "events.calendar_export",
"subscriptions.view", "subscriptions.create", "subscriptions.edit",
"subscriptions.cancel", "subscriptions.activate", "subscriptions.plans",
"financials.view", "financials.create", "financials.edit", "financials.delete",
"financials.export", "financials.payments",
"newsletters.view", "newsletters.create", "newsletters.edit", "newsletters.delete",
"newsletters.send", "newsletters.subscribers",
"bylaws.view", "bylaws.create", "bylaws.edit", "bylaws.delete", "bylaws.publish",
"gallery.view", "gallery.upload", "gallery.edit", "gallery.delete", "gallery.moderate",
"settings.view", "settings.edit", "settings.email_templates", "settings.storage",
"settings.logs",
],
"superadmin": [
# Superadmin gets ALL permissions
*[p["code"] for p in PERMISSIONS]
]
}
def seed_permissions():
"""Seed permissions and assign them to dynamic roles"""
db = SessionLocal()
try:
print("=" * 80)
print("🌱 PERMISSION SEEDING FOR DYNAMIC RBAC SYSTEM")
print("=" * 80)
# Step 1: Clear existing permissions and role_permissions
print("\n📦 Clearing existing permissions and role assignments...")
# Use raw SQL to ensure complete deletion
from sqlalchemy import text
db.execute(text("DELETE FROM role_permissions"))
db.commit()
db.execute(text("DELETE FROM permissions"))
db.commit()
# Verify they're cleared
rp_count = db.execute(text("SELECT COUNT(*) FROM role_permissions")).scalar()
p_count = db.execute(text("SELECT COUNT(*) FROM permissions")).scalar()
print(f"✓ Cleared role-permission mappings (verified: {rp_count} remaining)")
print(f"✓ Cleared permissions (verified: {p_count} remaining)")
if rp_count > 0 or p_count > 0:
print(f"\n⚠️ WARNING: Tables not fully cleared! Stopping.")
return
# Step 2: Create permissions
print(f"\n📝 Creating {len(PERMISSIONS)} permissions...")
permission_map = {} # Map code to permission object
for perm_data in PERMISSIONS:
permission = Permission(
code=perm_data["code"],
name=perm_data["name"],
description=perm_data["description"],
module=perm_data["module"]
)
db.add(permission)
permission_map[perm_data["code"]] = permission
db.commit()
print(f"✓ Created {len(PERMISSIONS)} permissions")
# Step 3: Get all roles from database
print("\n🔍 Fetching dynamic roles...")
roles = db.query(Role).all()
role_map = {role.code: role for role in roles}
print(f"✓ Found {len(roles)} roles: {', '.join(role_map.keys())}")
# Step 4: Assign permissions to roles
print("\n🔐 Assigning permissions to roles...")
from models import UserRole # Import for enum mapping
# Enum mapping for backward compatibility
role_enum_map = {
'guest': UserRole.guest,
'member': UserRole.member,
'admin': UserRole.admin,
'superadmin': UserRole.superadmin,
'finance': UserRole.finance # Finance has its own enum value
}
total_assigned = 0
for role_code, permission_codes in DEFAULT_ROLE_PERMISSIONS.items():
if role_code not in role_map:
print(f" ⚠️ Warning: Role '{role_code}' not found in database, skipping")
continue
role = role_map[role_code]
role_enum = role_enum_map.get(role_code, UserRole.guest)
for perm_code in permission_codes:
if perm_code not in permission_map:
print(f" ⚠️ Warning: Permission '{perm_code}' not found")
continue
role_permission = RolePermission(
role=role_enum, # Legacy enum for backward compatibility
role_id=role.id, # New dynamic role system
permission_id=permission_map[perm_code].id
)
db.add(role_permission)
total_assigned += 1
db.commit()
print(f"{role.name}: Assigned {len(permission_codes)} permissions")
# Step 5: Summary
print("\n" + "=" * 80)
print("📊 SEEDING SUMMARY")
print("=" * 80)
# Count permissions by module
modules = {}
for perm in PERMISSIONS:
module = perm["module"]
modules[module] = modules.get(module, 0) + 1
print("\nPermissions by module:")
for module, count in sorted(modules.items()):
print(f"{module.capitalize()}: {count} permissions")
print(f"\nTotal permissions created: {len(PERMISSIONS)}")
print(f"Total role-permission mappings: {total_assigned}")
print("\n✅ Permission seeding completed successfully!")
print("\nNext step: Restart backend server")
print("=" * 80)
except Exception as e:
db.rollback()
print(f"\n❌ Error seeding permissions: {str(e)}")
import traceback
traceback.print_exc()
raise
finally:
db.close()
if __name__ == "__main__":
seed_permissions()