178 Zeilen
6.2 KiB
Python
178 Zeilen
6.2 KiB
Python
"""Einmalige Datenmigration zu Multi-Tenancy.
|
|
|
|
Dieses Script:
|
|
1. Erstellt die AegisSight-Organisation
|
|
2. Erstellt eine permanente Lizenz fuer AegisSight
|
|
3. Weist bestehende Nutzer (rac00n, ch33tah) der AegisSight-Org zu
|
|
4. Setzt tenant_id auf alle bestehenden Daten
|
|
5. Erstellt Portal-Admin-Zugaenge
|
|
"""
|
|
import asyncio
|
|
import os
|
|
import sys
|
|
import shutil
|
|
from datetime import datetime
|
|
|
|
# Pfade fuer Imports
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
|
|
from config import DB_PATH, TIMEZONE
|
|
from database import init_db, get_db
|
|
from auth import hash_password
|
|
|
|
|
|
# E-Mail-Adressen fuer bestehende Nutzer
|
|
USER_EMAILS = {
|
|
"rac00n": os.environ.get("RACOON_EMAIL", "momohomma@googlemail.com"),
|
|
"ch33tah": os.environ.get("CHEETAH_EMAIL", "hendrik_gebhardt@gmx.de"),
|
|
}
|
|
|
|
|
|
async def migrate():
|
|
"""Fuehrt die Multi-Tenancy-Migration durch."""
|
|
|
|
# 1. Backup erstellen
|
|
if os.path.exists(DB_PATH):
|
|
backup_path = DB_PATH + f".backup-{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
shutil.copy2(DB_PATH, backup_path)
|
|
print(f"Backup erstellt: {backup_path}")
|
|
else:
|
|
print("Keine bestehende Datenbank gefunden. Frische Installation.")
|
|
|
|
# 2. Schema-Migration (init_db erstellt neue Tabellen und fuegt Spalten hinzu)
|
|
await init_db()
|
|
print("Schema-Migration abgeschlossen.")
|
|
|
|
db = await get_db()
|
|
try:
|
|
# 3. Pruefen ob Migration bereits gelaufen ist
|
|
cursor = await db.execute("SELECT COUNT(*) as cnt FROM organizations")
|
|
org_count = (await cursor.fetchone())["cnt"]
|
|
if org_count > 0:
|
|
print("Migration wurde bereits durchgefuehrt. Abbruch.")
|
|
return
|
|
|
|
# 4. AegisSight-Organisation anlegen
|
|
now = datetime.now(TIMEZONE).isoformat()
|
|
cursor = await db.execute(
|
|
"""INSERT INTO organizations (name, slug, is_active, created_at, updated_at)
|
|
VALUES (?, ?, 1, ?, ?)""",
|
|
("AegisSight", "aegissight", now, now),
|
|
)
|
|
aegis_org_id = cursor.lastrowid
|
|
print(f"Organisation AegisSight angelegt (ID: {aegis_org_id})")
|
|
|
|
# 5. Permanente Lizenz fuer AegisSight
|
|
await db.execute(
|
|
"""INSERT INTO licenses (organization_id, license_type, max_users, valid_from, valid_until, status, notes)
|
|
VALUES (?, 'permanent', 50, ?, NULL, 'active', 'Interne AegisSight-Lizenz')""",
|
|
(aegis_org_id, now),
|
|
)
|
|
print("Permanente Lizenz fuer AegisSight erstellt")
|
|
|
|
# 6. Bestehende Nutzer der AegisSight-Org zuweisen
|
|
cursor = await db.execute("SELECT id, username FROM users")
|
|
users = await cursor.fetchall()
|
|
|
|
for user in users:
|
|
username = user["username"]
|
|
email = USER_EMAILS.get(username, f"{username}@aegis-sight.de")
|
|
|
|
await db.execute(
|
|
"""UPDATE users SET
|
|
organization_id = ?,
|
|
role = 'org_admin',
|
|
is_active = 1,
|
|
email = ?
|
|
WHERE id = ?""",
|
|
(aegis_org_id, email, user["id"]),
|
|
)
|
|
print(f"Nutzer '{username}' -> AegisSight (org_admin, email: {email})")
|
|
|
|
# 7. tenant_id auf alle bestehenden Incidents setzen
|
|
await db.execute(
|
|
"UPDATE incidents SET tenant_id = ? WHERE tenant_id IS NULL",
|
|
(aegis_org_id,),
|
|
)
|
|
cursor = await db.execute("SELECT changes()")
|
|
changes = (await cursor.fetchone())[0]
|
|
print(f"{changes} Incidents mit tenant_id versehen")
|
|
|
|
# 8. tenant_id auf alle bestehenden Articles setzen
|
|
await db.execute(
|
|
"UPDATE articles SET tenant_id = ? WHERE tenant_id IS NULL",
|
|
(aegis_org_id,),
|
|
)
|
|
cursor = await db.execute("SELECT changes()")
|
|
changes = (await cursor.fetchone())[0]
|
|
print(f"{changes} Articles mit tenant_id versehen")
|
|
|
|
# 9. tenant_id auf alle bestehenden fact_checks setzen
|
|
await db.execute(
|
|
"UPDATE fact_checks SET tenant_id = ? WHERE tenant_id IS NULL",
|
|
(aegis_org_id,),
|
|
)
|
|
|
|
# 10. tenant_id auf alle bestehenden refresh_log setzen
|
|
await db.execute(
|
|
"UPDATE refresh_log SET tenant_id = ? WHERE tenant_id IS NULL",
|
|
(aegis_org_id,),
|
|
)
|
|
|
|
# 11. tenant_id auf alle bestehenden incident_snapshots setzen
|
|
await db.execute(
|
|
"UPDATE incident_snapshots SET tenant_id = ? WHERE tenant_id IS NULL",
|
|
(aegis_org_id,),
|
|
)
|
|
|
|
# 12. tenant_id auf alle bestehenden notifications setzen
|
|
await db.execute(
|
|
"UPDATE notifications SET tenant_id = ? WHERE tenant_id IS NULL",
|
|
(aegis_org_id,),
|
|
)
|
|
|
|
# 13. System-Quellen bleiben global (tenant_id=NULL)
|
|
# Nur nutzer-erstellte Quellen bekommen tenant_id
|
|
await db.execute(
|
|
"UPDATE sources SET tenant_id = ? WHERE added_by != 'system' AND tenant_id IS NULL",
|
|
(aegis_org_id,),
|
|
)
|
|
print("Nutzer-Quellen mit tenant_id versehen (System-Quellen bleiben global)")
|
|
|
|
# 14. Portal-Admin-Zugaenge anlegen
|
|
print("\n--- Portal-Admin-Zugaenge ---")
|
|
portal_users = ["rac00n", "ch33tah"]
|
|
for pu in portal_users:
|
|
# Pruefen ob schon existiert
|
|
cursor = await db.execute(
|
|
"SELECT id FROM portal_admins WHERE username = ?", (pu,)
|
|
)
|
|
if await cursor.fetchone():
|
|
print(f"Portal-Admin '{pu}' existiert bereits")
|
|
continue
|
|
|
|
# Passwort generieren
|
|
import secrets
|
|
import string
|
|
password = ''.join(secrets.choice(string.ascii_letters + string.digits + "!@#$%&*") for _ in range(16))
|
|
pw_hash = hash_password(password)
|
|
|
|
await db.execute(
|
|
"INSERT INTO portal_admins (username, password_hash) VALUES (?, ?)",
|
|
(pu, pw_hash),
|
|
)
|
|
print(f"Portal-Admin '{pu}' erstellt - Passwort: {password}")
|
|
|
|
await db.commit()
|
|
print("\nMigration erfolgreich abgeschlossen!")
|
|
|
|
except Exception as e:
|
|
print(f"\nFEHLER bei Migration: {e}")
|
|
raise
|
|
finally:
|
|
await db.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(migrate())
|