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()