285 lines
15 KiB
Python
Executable File
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()
|