#!/usr/bin/env python3 """ Security Migration Script Migrates existing SHA-256 passwords to bcrypt MUST be run after upgrading to bcrypt-based authentication """ import sys import os sys.path.insert(0, os.path.dirname(__file__)) from postgresql_models import SessionLocal, User import bcrypt import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def migrate_passwords(): """ Migrate all SHA-256 passwords to bcrypt This should be run ONCE after upgrading to bcrypt """ db = SessionLocal() try: users = db.query(User).all() logger.info(f"Found {len(users)} users to check") migrated = 0 for user in users: # Check if password is already bcrypt (starts with $2b$) if user.password_hash.startswith('$2b$') or user.password_hash.startswith('$2a$'): logger.info(f"User {user.username} already using bcrypt - skipping") continue # Check if this is a SHA-256 hash (64 hex characters) if len(user.password_hash) == 64 and all(c in '0123456789abcdef' for c in user.password_hash): logger.warning(f"User {user.username} has SHA-256 hash - CANNOT migrate automatically") logger.warning(f" User must reset password or admin must set new password") logger.warning(f" To set new password: user.set_password('new_password')") else: logger.warning(f"User {user.username} has unknown hash format: {user.password_hash[:20]}...") logger.info(f"Migration complete. {migrated} passwords migrated.") logger.warning("") logger.warning("IMPORTANT: Users with SHA-256 passwords must reset their passwords!") logger.warning(" Option 1: Users can request password reset") logger.warning(" Option 2: Admin can manually set new passwords using user.set_password()") except Exception as e: logger.error(f"Migration failed: {e}") db.rollback() return 1 finally: db.close() return 0 def create_default_admin(username='admin', password=None): """ Create a default admin user with bcrypt password Use this to create the first admin account """ db = SessionLocal() try: # Check if user exists existing = db.query(User).filter(User.username == username).first() if existing: logger.error(f"User {username} already exists") return 1 if not password: logger.error("Password is required") return 1 # Create new admin user admin = User( username=username, role='admin', permissions='view,edit,modify,settings', active=True ) admin.set_password(password) db.add(admin) db.commit() logger.info(f"✓ Admin user '{username}' created successfully with bcrypt password") return 0 except Exception as e: logger.error(f"Failed to create admin: {e}") db.rollback() return 1 finally: db.close() if __name__ == '__main__': import argparse parser = argparse.ArgumentParser(description='Security migration tool') parser.add_argument('--migrate', action='store_true', help='Migrate SHA-256 passwords to bcrypt') parser.add_argument('--create-admin', action='store_true', help='Create default admin user') parser.add_argument('--username', default='admin', help='Username for new admin') parser.add_argument('--password', help='Password for new admin') args = parser.parse_args() if args.create_admin: if not args.password: print("ERROR: --password required when creating admin") sys.exit(1) sys.exit(create_default_admin(args.username, args.password)) if args.migrate: sys.exit(migrate_passwords()) parser.print_help() sys.exit(1)