225 lines
8.4 KiB
Python

import os
import json
import secrets
from datetime import timedelta
from pathlib import Path
# Basisverzeichnis der Anwendung für relative Pfade
BASE_DIR = Path(__file__).resolve().parent.parent
PROJECT_ROOT = BASE_DIR.parent
# Laden von Umgebungsvariablen, falls vorhanden
def get_env_variable(var_name, default=None):
"""Umgebungsvariablen abrufen mit Fallback auf Default-Werte"""
return os.environ.get(var_name, default)
# Sichere Konfiguration - Verwende Umgebungsvariablen oder generiere Secret Key
SECRET_KEY = get_env_variable("MYP_SECRET_KEY", secrets.token_hex(24))
# Datenbankpfad mit korrekter relativer Pfadauflösung
DATABASE_PATH = os.path.join(PROJECT_ROOT, "database", "myp.db")
# Tapo-Zugangsdaten aus Umgebungsvariablen laden
TAPO_USERNAME = get_env_variable("MYP_TAPO_USERNAME", "")
TAPO_PASSWORD = get_env_variable("MYP_TAPO_PASSWORD", "")
# Drucker-Konfiguration
PRINTERS = {
"Printer 1": {"ip": "192.168.0.100"},
"Printer 2": {"ip": "192.168.0.101"},
"Printer 3": {"ip": "192.168.0.102"},
"Printer 4": {"ip": "192.168.0.103"},
"Printer 5": {"ip": "192.168.0.104"},
"Printer 6": {"ip": "192.168.0.106"}
}
# Logging-Konfiguration
LOG_DIR = os.path.join(PROJECT_ROOT, "logs")
LOG_SUBDIRS = ["app", "scheduler", "auth", "jobs", "printers", "errors"]
LOG_LEVEL = get_env_variable("MYP_LOG_LEVEL", "INFO")
LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
# Flask-Konfiguration
FLASK_HOST = get_env_variable("MYP_FLASK_HOST", "0.0.0.0")
FLASK_PORT = int(get_env_variable("MYP_FLASK_PORT", 443))
FLASK_FALLBACK_PORT = int(get_env_variable("MYP_FLASK_FALLBACK_PORT", 80))
FLASK_DEBUG = get_env_variable("MYP_FLASK_DEBUG", "True").lower() in ("true", "1", "yes")
SESSION_LIFETIME = timedelta(days=int(get_env_variable("MYP_SESSION_DAYS", 7)))
# SSL-Konfiguration
SSL_ENABLED = get_env_variable("MYP_SSL_ENABLED", "True").lower() in ("true", "1", "yes")
SSL_CERT_PATH = os.path.join(PROJECT_ROOT, "certs", "myp.crt")
SSL_KEY_PATH = os.path.join(PROJECT_ROOT, "certs", "myp.key")
SSL_HOSTNAME = get_env_variable("MYP_SSL_HOSTNAME", "raspberrypi")
# Scheduler-Konfiguration
SCHEDULER_INTERVAL = int(get_env_variable("MYP_SCHEDULER_INTERVAL", 60)) # Sekunden
SCHEDULER_ENABLED = get_env_variable("MYP_SCHEDULER_ENABLED", "True").lower() in ("true", "1", "yes")
# Datenbank-Konfiguration
DB_ENGINE = f"sqlite:///{DATABASE_PATH}"
def get_log_file(category: str) -> str:
"""
Gibt den Pfad zur Log-Datei für eine bestimmte Kategorie zurück.
Args:
category: Log-Kategorie (app, scheduler, auth, jobs, printers, errors)
Returns:
str: Pfad zur Log-Datei
"""
if category not in LOG_SUBDIRS:
category = "app"
return os.path.join(LOG_DIR, category, f"{category}.log")
def ensure_log_directories():
"""Erstellt alle erforderlichen Log-Verzeichnisse."""
os.makedirs(LOG_DIR, exist_ok=True)
for subdir in LOG_SUBDIRS:
os.makedirs(os.path.join(LOG_DIR, subdir), exist_ok=True)
def ensure_database_directory():
"""Erstellt das Datenbank-Verzeichnis."""
db_dir = os.path.dirname(DATABASE_PATH)
if db_dir:
os.makedirs(db_dir, exist_ok=True)
def ensure_ssl_directory():
"""Erstellt das SSL-Verzeichnis, falls es nicht existiert."""
ssl_dir = os.path.dirname(SSL_CERT_PATH)
if ssl_dir and not os.path.exists(ssl_dir):
os.makedirs(ssl_dir, exist_ok=True)
def get_ssl_context():
"""
Gibt den SSL-Kontext für Flask zurück, wenn SSL aktiviert ist.
Returns:
tuple oder None: Tuple mit Zertifikat- und Schlüsselpfad, wenn SSL aktiviert ist, sonst None
"""
if not SSL_ENABLED:
return None
# Wenn Zertifikate nicht existieren, diese automatisch erstellen
if not os.path.exists(SSL_CERT_PATH) or not os.path.exists(SSL_KEY_PATH):
ensure_ssl_directory()
# Im Entwicklungsmodus versuchen wir, einfache Zertifikate zu erstellen
if FLASK_DEBUG:
print("SSL-Zertifikate nicht gefunden. Erstelle einfache selbstsignierte Zertifikate...")
try:
# Einfache Zertifikate mit Python erstellen
create_simple_ssl_cert()
# Prüfen, ob die Zertifikate erfolgreich erstellt wurden
if not os.path.exists(SSL_CERT_PATH) or not os.path.exists(SSL_KEY_PATH):
print("Konnte keine SSL-Zertifikate erstellen.")
return None
except Exception as e:
print(f"Fehler beim Erstellen der SSL-Zertifikate: {e}")
return None
else:
print("WARNUNG: SSL-Zertifikate nicht gefunden und Nicht-Debug-Modus. SSL wird deaktiviert.")
return None
return (SSL_CERT_PATH, SSL_KEY_PATH)
def create_simple_ssl_cert():
"""
Erstellt ein einfaches selbstsigniertes Zertifikat mit Python.
Dies ist ein Fallback, falls OpenSSL nicht verfügbar ist.
"""
try:
import datetime
import socket
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
# Generiere privaten Schlüssel
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=4096, # Stärkere Schlüsselgröße
)
# Schreibe privaten Schlüssel
with open(SSL_KEY_PATH, "wb") as f:
f.write(private_key.private_bytes(
encoding=Encoding.PEM,
format=PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=NoEncryption()
))
# Aktuelles Datum und Ablaufdatum berechnen
now = datetime.datetime.now()
valid_until = now + datetime.timedelta(days=3650) # 10 Jahre gültig
# Erstelle Zertifikat mit erweiterten Attributen
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, SSL_HOSTNAME),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Mercedes-Benz AG"),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Werk 040 Berlin"),
x509.NameAttribute(NameOID.COUNTRY_NAME, "DE"),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Berlin"),
x509.NameAttribute(NameOID.LOCALITY_NAME, "Berlin")
])
cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
issuer
).public_key(
private_key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
now
).not_valid_after(
valid_until
).add_extension(
x509.SubjectAlternativeName([
x509.DNSName(SSL_HOSTNAME),
x509.DNSName("localhost"),
x509.IPAddress(socket.inet_aton("127.0.0.1"))
]),
critical=False,
).add_extension(
x509.BasicConstraints(ca=True, path_length=None), critical=True
).add_extension(
x509.KeyUsage(
digital_signature=True,
content_commitment=False,
key_encipherment=True,
data_encipherment=False,
key_agreement=False,
key_cert_sign=True,
crl_sign=True,
encipher_only=False,
decipher_only=False
), critical=True
).add_extension(
x509.ExtendedKeyUsage([
x509.oid.ExtendedKeyUsageOID.SERVER_AUTH,
x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH
]), critical=False
).sign(private_key, hashes.SHA256())
# Schreibe Zertifikat
with open(SSL_CERT_PATH, "wb") as f:
f.write(cert.public_bytes(Encoding.PEM))
print(f"Verbessertes selbstsigniertes Zertifikat erstellt: {SSL_CERT_PATH}")
print(f"Gültig bis: {valid_until.strftime('%d.%m.%Y')}")
except ImportError:
print("Konnte keine SSL-Zertifikate erstellen: cryptography-Paket nicht installiert")
return None
except Exception as e:
print(f"Fehler beim Erstellen der SSL-Zertifikate: {e}")
return None