HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //opt/kw-schueler.bak.21032025/fix_school_admin.py
#!/usr/bin/env python3
import bcrypt
import mysql.connector
import logging
import argparse
import sys
import os
import toml

# Logging konfigurieren
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler("fix_school_admin.log")
    ]
)

def hash_password(password):
    """Hasht ein Passwort mit bcrypt."""
    salt = bcrypt.gensalt(12)  # 12 Runden für Sicherheit
    hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
    return hashed.decode('utf-8')  # Speichere den Hash als String

def get_db_connection():
    """Stellt eine Verbindung zur Remote-MySQL-Datenbank her."""
    try:
        # Pfad zur Secrets-Datei
        secrets_path = os.path.join('/opt/klausurenweb-schueler', '.streamlit', 'secrets.toml')
        
        # Lese die Secrets-Datei
        if os.path.exists(secrets_path):
            with open(secrets_path, 'r') as f:
                secrets = toml.load(f)
        else:
            logging.error(f"Secrets-Datei nicht gefunden unter: {secrets_path}")
            return None
        
        # Verbindungsdaten aus den Secrets
        mysql_secrets = secrets.get("mysql", {})
        
        if not mysql_secrets:
            logging.error("MySQL-Konfiguration nicht gefunden in der Secrets-Datei")
            return None
        
        # Log-Verbindungsdetails (ohne Passwort)
        logging.info(f"Versuche Verbindung zu MySQL: host={mysql_secrets.get('host')}, user={mysql_secrets.get('user')}, database={mysql_secrets.get('database')}")
        
        connection = mysql.connector.connect(
            host=mysql_secrets.get("host"),
            user=mysql_secrets.get("user"),
            password=mysql_secrets.get("password"),
            database=mysql_secrets.get("database")
        )
        
        if connection.is_connected():
            logging.info(f"Verbunden mit MySQL Server auf {mysql_secrets.get('host')}")
            return connection
    except Exception as e:
        logging.error(f"Fehler bei der Datenbankverbindung: {e}")
    
    return None

def update_school_admin_password(admin_username, new_password):
    """Aktualisiert das Passwort eines Schuladmins."""
    conn = get_db_connection()
    if not conn:
        logging.error("Keine Datenbankverbindung möglich")
        return False
    
    try:
        cursor = conn.cursor(dictionary=True)
        
        # Prüfe, ob der Admin existiert
        cursor.execute("SELECT id FROM school_admins WHERE username = %s", (admin_username,))
        admin = cursor.fetchone()
        
        if not admin:
            logging.error(f"Schuladmin mit Benutzername '{admin_username}' nicht gefunden")
            return False
        
        # Neues Passwort hashen
        hashed_password = hash_password(new_password)
        
        # Passwort aktualisieren
        cursor.execute(
            "UPDATE school_admins SET password_hash = %s WHERE username = %s",
            (hashed_password, admin_username)
        )
        conn.commit()
        
        logging.info(f"Passwort für Schuladmin '{admin_username}' erfolgreich aktualisiert")
        return True
    except Exception as e:
        logging.error(f"Fehler beim Aktualisieren des Passworts: {e}")
        return False
    finally:
        if conn and conn.is_connected():
            cursor.close()
            conn.close()

def list_school_admins():
    """Listet alle Schuladministratoren auf."""
    conn = get_db_connection()
    if not conn:
        logging.error("Keine Datenbankverbindung möglich")
        return
    
    try:
        cursor = conn.cursor(dictionary=True)
        cursor.execute(
            """
            SELECT sa.id, sa.username, sa.email, s.name as school_name
            FROM school_admins sa
            JOIN schools s ON sa.school_id = s.id
            """
        )
        admins = cursor.fetchall()
        
        if not admins:
            logging.info("Keine Schuladministratoren gefunden")
            return []
        
        logging.info("Vorhandene Schuladministratoren:")
        for admin in admins:
            logging.info(f"ID: {admin['id']}, Benutzername: {admin['username']}, E-Mail: {admin['email']}, Schule: {admin['school_name']}")
        
        return admins
    except Exception as e:
        logging.error(f"Fehler beim Auflisten der Schuladministratoren: {e}")
        return []
    finally:
        if conn and conn.is_connected():
            cursor.close()
            conn.close()

def create_school_admin(username, email, password, school_id):
    """Erstellt einen neuen Schuladministrator."""
    conn = get_db_connection()
    if not conn:
        logging.error("Keine Datenbankverbindung möglich")
        return False
    
    try:
        cursor = conn.cursor(dictionary=True)
        
        # Prüfe, ob die Schule existiert
        cursor.execute("SELECT id, name FROM schools WHERE id = %s", (school_id,))
        school = cursor.fetchone()
        
        if not school:
            logging.error(f"Schule mit ID '{school_id}' nicht gefunden")
            return False
        
        # Prüfe, ob Benutzername oder E-Mail bereits existieren
        cursor.execute("SELECT id FROM school_admins WHERE username = %s OR email = %s", (username, email))
        existing = cursor.fetchone()
        
        if existing:
            logging.error(f"Ein Schuladmin mit diesem Benutzernamen oder dieser E-Mail existiert bereits")
            return False
        
        # Passwort hashen
        hashed_password = hash_password(password)
        
        # Neuen Schuladmin erstellen
        cursor.execute(
            """
            INSERT INTO school_admins (username, email, password_hash, school_id, is_active, created_at)
            VALUES (%s, %s, %s, %s, TRUE, NOW())
            """,
            (username, email, hashed_password, school_id)
        )
        
        conn.commit()
        
        logging.info(f"Schuladmin '{username}' für Schule '{school['name']}' erfolgreich erstellt")
        return True
    except Exception as e:
        logging.error(f"Fehler beim Erstellen des Schuladministrators: {e}")
        return False
    finally:
        if conn and conn.is_connected():
            cursor.close()
            conn.close()

def list_schools():
    """Listet alle verfügbaren Schulen auf."""
    conn = get_db_connection()
    if not conn:
        logging.error("Keine Datenbankverbindung möglich")
        return []
    
    try:
        cursor = conn.cursor(dictionary=True)
        cursor.execute("SELECT id, name, code FROM schools")
        schools = cursor.fetchall()
        
        if not schools:
            logging.info("Keine Schulen gefunden")
            return []
        
        logging.info("Verfügbare Schulen:")
        for school in schools:
            logging.info(f"ID: {school['id']}, Name: {school['name']}, Code: {school['code']}")
        
        return schools
    except Exception as e:
        logging.error(f"Fehler beim Auflisten der Schulen: {e}")
        return []
    finally:
        if conn and conn.is_connected():
            cursor.close()
            conn.close()

def initialize_tables():
    """Überprüft und initialisiert die notwendigen Tabellen, falls sie nicht existieren."""
    conn = get_db_connection()
    if not conn:
        logging.error("Keine Datenbankverbindung möglich")
        return False
    
    try:
        cursor = conn.cursor()
        
        # Überprüfe, ob die schools-Tabelle existiert
        cursor.execute("SHOW TABLES LIKE 'schools'")
        if not cursor.fetchone():
            logging.info("Erstelle schools-Tabelle...")
            cursor.execute("""
            CREATE TABLE schools (
                id INT AUTO_INCREMENT PRIMARY KEY,
                name VARCHAR(100) NOT NULL,
                code VARCHAR(10) NOT NULL UNIQUE,
                address TEXT,
                contact_email VARCHAR(100),
                contact_phone VARCHAR(20),
                is_active BOOLEAN DEFAULT TRUE,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
            )
            """)
            
            # Beispielschule erstellen
            cursor.execute("""
            INSERT INTO schools (name, code, address, contact_email)
            VALUES ('Demo Schule', 'DEMO', 'Schulstraße 1, 12345 Musterstadt', 'info@demo-schule.de')
            """)
        
        # Überprüfe, ob die school_admins-Tabelle existiert
        cursor.execute("SHOW TABLES LIKE 'school_admins'")
        if not cursor.fetchone():
            logging.info("Erstelle school_admins-Tabelle...")
            cursor.execute("""
            CREATE TABLE school_admins (
                id INT AUTO_INCREMENT PRIMARY KEY,
                username VARCHAR(50) NOT NULL UNIQUE,
                email VARCHAR(100) NOT NULL UNIQUE,
                password_hash VARCHAR(255) NOT NULL,
                school_id INT NOT NULL,
                is_active BOOLEAN DEFAULT TRUE,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE
            )
            """)
        
        conn.commit()
        logging.info("Tabellen wurden überprüft und ggf. initialisiert")
        return True
    except Exception as e:
        logging.error(f"Fehler beim Initialisieren der Tabellen: {e}")
        return False
    finally:
        if conn and conn.is_connected():
            cursor.close()
            conn.close()

def main():
    parser = argparse.ArgumentParser(description='Schuladmin-Verwaltung')
    subparsers = parser.add_subparsers(dest='command', help='Verfügbare Befehle')
    
    # Befehl zum Auflisten von Schuladmins
    subparsers.add_parser('list_admins', help='Schuladministratoren auflisten')
    
    # Befehl zum Auflisten von Schulen
    subparsers.add_parser('list_schools', help='Schulen auflisten')
    
    # Befehl zur Initialisierung der Tabellen
    subparsers.add_parser('init', help='Tabellen initialisieren')
    
    # Befehl zum Aktualisieren des Passworts
    update_parser = subparsers.add_parser('update', help='Passwort eines Schuladmins aktualisieren')
    update_parser.add_argument('username', help='Benutzername des Schuladmins')
    update_parser.add_argument('password', help='Neues Passwort')
    
    # Befehl zum Erstellen eines neuen Schuladmins
    create_parser = subparsers.add_parser('create', help='Neuen Schuladmin erstellen')
    create_parser.add_argument('username', help='Benutzername')
    create_parser.add_argument('email', help='E-Mail-Adresse')
    create_parser.add_argument('password', help='Passwort')
    create_parser.add_argument('school_id', type=int, help='Schul-ID')
    
    args = parser.parse_args()
    
    if args.command == 'list_admins':
        list_school_admins()
    elif args.command == 'list_schools':
        list_schools()
    elif args.command == 'init':
        initialize_tables()
    elif args.command == 'update':
        if update_school_admin_password(args.username, args.password):
            print(f"Passwort für {args.username} erfolgreich aktualisiert.")
        else:
            print(f"Fehler beim Aktualisieren des Passworts für {args.username}.")
            sys.exit(1)
    elif args.command == 'create':
        if create_school_admin(args.username, args.email, args.password, args.school_id):
            print(f"Schuladmin {args.username} erfolgreich erstellt.")
        else:
            print(f"Fehler beim Erstellen des Schuladmins {args.username}.")
            sys.exit(1)
    else:
        parser.print_help()

if __name__ == "__main__":
    main()