- Removed `COMMON_ERRORS.md` file to streamline documentation. - Added `Flask-Limiter` for rate limiting and `redis` for session management in `requirements.txt`. - Expanded `ROADMAP.md` to include completed security features and planned enhancements for version 2.2. - Enhanced `setup_myp.sh` for ultra-secure kiosk installation, including system hardening and security configurations. - Updated `app.py` to integrate CSRF protection and improved logging setup. - Refactored user model to include username and active status for better user management. - Improved job scheduler with uptime tracking and task management features. - Updated various templates for a more cohesive user interface and experience.
360 lines
14 KiB
Python
360 lines
14 KiB
Python
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
|
|
from flask_login import current_user, login_required
|
|
from datetime import datetime
|
|
|
|
from utils.logging_config import get_logger
|
|
from models import User, get_db_session
|
|
|
|
# Logger für Benutzeraktionen
|
|
user_logger = get_logger("user")
|
|
|
|
# Blueprint erstellen
|
|
user_bp = Blueprint('user', __name__, url_prefix='/user')
|
|
|
|
@user_bp.route("/profile", methods=["GET"])
|
|
@login_required
|
|
def profile():
|
|
"""Profil-Seite anzeigen"""
|
|
user_logger.info(f"Benutzer {current_user.username} hat seine Profilseite aufgerufen")
|
|
return render_template("profile.html", user=current_user)
|
|
|
|
@user_bp.route("/settings", methods=["GET"])
|
|
@login_required
|
|
def settings():
|
|
"""Einstellungen-Seite anzeigen"""
|
|
user_logger.info(f"Benutzer {current_user.username} hat seine Einstellungsseite aufgerufen")
|
|
return render_template("settings.html", user=current_user)
|
|
|
|
@user_bp.route("/update-profile", methods=["POST"])
|
|
@login_required
|
|
def update_profile():
|
|
"""Benutzerprofilinformationen aktualisieren"""
|
|
try:
|
|
# Überprüfen, ob es sich um eine JSON-Anfrage handelt
|
|
is_json_request = request.is_json or request.headers.get('Content-Type') == 'application/json'
|
|
|
|
if is_json_request:
|
|
data = request.get_json()
|
|
name = data.get("name")
|
|
email = data.get("email")
|
|
department = data.get("department")
|
|
position = data.get("position")
|
|
phone = data.get("phone")
|
|
else:
|
|
name = request.form.get("name")
|
|
email = request.form.get("email")
|
|
department = request.form.get("department")
|
|
position = request.form.get("position")
|
|
phone = request.form.get("phone")
|
|
|
|
db_session = get_db_session()
|
|
user = db_session.query(User).filter(User.id == current_user.id).first()
|
|
|
|
if user:
|
|
# Aktualisiere die Benutzerinformationen
|
|
if name:
|
|
user.name = name
|
|
if email:
|
|
user.email = email
|
|
if department:
|
|
user.department = department
|
|
if position:
|
|
user.position = position
|
|
if phone:
|
|
user.phone = phone
|
|
|
|
user.updated_at = datetime.now()
|
|
db_session.commit()
|
|
user_logger.info(f"Benutzer {current_user.username} hat sein Profil aktualisiert")
|
|
|
|
if is_json_request:
|
|
return jsonify({
|
|
"success": True,
|
|
"message": "Profil erfolgreich aktualisiert"
|
|
})
|
|
else:
|
|
flash("Profil erfolgreich aktualisiert", "success")
|
|
return redirect(url_for("user.profile"))
|
|
else:
|
|
error = "Benutzer nicht gefunden."
|
|
if is_json_request:
|
|
return jsonify({"error": error}), 404
|
|
else:
|
|
flash(error, "error")
|
|
return redirect(url_for("user.profile"))
|
|
|
|
except Exception as e:
|
|
error = f"Fehler beim Aktualisieren des Profils: {str(e)}"
|
|
user_logger.error(error)
|
|
if request.is_json:
|
|
return jsonify({"error": error}), 500
|
|
else:
|
|
flash(error, "error")
|
|
return redirect(url_for("user.profile"))
|
|
finally:
|
|
db_session.close()
|
|
|
|
@user_bp.route("/update-settings", methods=["POST"])
|
|
@login_required
|
|
def update_settings():
|
|
"""Benutzereinstellungen aktualisieren"""
|
|
try:
|
|
# Überprüfen, ob es sich um eine JSON-Anfrage handelt
|
|
is_json_request = request.is_json or request.headers.get('Content-Type') == 'application/json'
|
|
|
|
# Einstellungen aus der Anfrage extrahieren
|
|
if is_json_request:
|
|
data = request.get_json()
|
|
theme = data.get("theme")
|
|
reduced_motion = data.get("reduced_motion", False)
|
|
contrast = data.get("contrast", "normal")
|
|
notifications = data.get("notifications", {})
|
|
privacy = data.get("privacy", {})
|
|
else:
|
|
theme = request.form.get("theme", "system")
|
|
reduced_motion = request.form.get("reduced_motion") == "on"
|
|
contrast = request.form.get("contrast", "normal")
|
|
notifications = {
|
|
"new_jobs": request.form.get("notify_new_jobs") == "on",
|
|
"job_updates": request.form.get("notify_job_updates") == "on",
|
|
"system": request.form.get("notify_system") == "on",
|
|
"email": request.form.get("notify_email") == "on"
|
|
}
|
|
privacy = {
|
|
"activity_logs": request.form.get("activity_logs") == "on",
|
|
"two_factor": request.form.get("two_factor") == "on",
|
|
"auto_logout": request.form.get("auto_logout", "60")
|
|
}
|
|
|
|
db_session = get_db_session()
|
|
user = db_session.query(User).filter(User.id == current_user.id).first()
|
|
|
|
if user:
|
|
# Erstelle ein Einstellungs-Dictionary, das wir als JSON speichern können
|
|
settings = {
|
|
"theme": theme,
|
|
"reduced_motion": reduced_motion,
|
|
"contrast": contrast,
|
|
"notifications": notifications,
|
|
"privacy": privacy,
|
|
"last_updated": datetime.now().isoformat()
|
|
}
|
|
|
|
# In einer echten Anwendung würden wir die Einstellungen in der Datenbank speichern
|
|
# Hier simulieren wir dies durch Aktualisierung des User-Objekts
|
|
|
|
# Wenn die User-Tabelle eine settings-Spalte hat, würden wir diese verwenden
|
|
# user.settings = json.dumps(settings)
|
|
|
|
# Für Demonstrationszwecke speichern wir die letzten Einstellungen im Session-Cookie
|
|
# (In einer Produktionsumgebung würde man dies in der Datenbank speichern)
|
|
from flask import session
|
|
session['user_settings'] = settings
|
|
|
|
user.updated_at = datetime.now()
|
|
db_session.commit()
|
|
|
|
user_logger.info(f"Benutzer {current_user.username} hat seine Einstellungen aktualisiert")
|
|
|
|
if is_json_request:
|
|
return jsonify({
|
|
"success": True,
|
|
"message": "Einstellungen erfolgreich aktualisiert",
|
|
"settings": settings
|
|
})
|
|
else:
|
|
flash("Einstellungen erfolgreich aktualisiert", "success")
|
|
return redirect(url_for("user.settings"))
|
|
else:
|
|
error = "Benutzer nicht gefunden."
|
|
if is_json_request:
|
|
return jsonify({"error": error}), 404
|
|
else:
|
|
flash(error, "error")
|
|
return redirect(url_for("user.settings"))
|
|
|
|
except Exception as e:
|
|
error = f"Fehler beim Aktualisieren der Einstellungen: {str(e)}"
|
|
user_logger.error(error)
|
|
if request.is_json:
|
|
return jsonify({"error": error}), 500
|
|
else:
|
|
flash(error, "error")
|
|
return redirect(url_for("user.settings"))
|
|
finally:
|
|
db_session.close()
|
|
|
|
@user_bp.route("/change-password", methods=["POST"])
|
|
@login_required
|
|
def change_password():
|
|
"""Benutzerpasswort ändern"""
|
|
try:
|
|
# Überprüfen, ob es sich um eine JSON-Anfrage handelt
|
|
is_json_request = request.is_json or request.headers.get('Content-Type') == 'application/json'
|
|
|
|
if is_json_request:
|
|
data = request.get_json()
|
|
current_password = data.get("current_password")
|
|
new_password = data.get("new_password")
|
|
confirm_password = data.get("confirm_password")
|
|
else:
|
|
current_password = request.form.get("current_password")
|
|
new_password = request.form.get("new_password")
|
|
confirm_password = request.form.get("confirm_password")
|
|
|
|
# Prüfen, ob alle Felder ausgefüllt sind
|
|
if not current_password or not new_password or not confirm_password:
|
|
error = "Alle Passwortfelder müssen ausgefüllt sein."
|
|
if is_json_request:
|
|
return jsonify({"error": error}), 400
|
|
else:
|
|
flash(error, "error")
|
|
return redirect(url_for("user.profile"))
|
|
|
|
# Prüfen, ob das neue Passwort und die Bestätigung übereinstimmen
|
|
if new_password != confirm_password:
|
|
error = "Das neue Passwort und die Bestätigung stimmen nicht überein."
|
|
if is_json_request:
|
|
return jsonify({"error": error}), 400
|
|
else:
|
|
flash(error, "error")
|
|
return redirect(url_for("user.profile"))
|
|
|
|
db_session = get_db_session()
|
|
user = db_session.query(User).filter(User.id == current_user.id).first()
|
|
|
|
if user and user.check_password(current_password):
|
|
# Passwort aktualisieren
|
|
user.set_password(new_password)
|
|
user.updated_at = datetime.now()
|
|
db_session.commit()
|
|
|
|
user_logger.info(f"Benutzer {current_user.username} hat sein Passwort geändert")
|
|
|
|
if is_json_request:
|
|
return jsonify({
|
|
"success": True,
|
|
"message": "Passwort erfolgreich geändert"
|
|
})
|
|
else:
|
|
flash("Passwort erfolgreich geändert", "success")
|
|
return redirect(url_for("user.profile"))
|
|
else:
|
|
error = "Das aktuelle Passwort ist nicht korrekt."
|
|
if is_json_request:
|
|
return jsonify({"error": error}), 401
|
|
else:
|
|
flash(error, "error")
|
|
return redirect(url_for("user.profile"))
|
|
|
|
except Exception as e:
|
|
error = f"Fehler beim Ändern des Passworts: {str(e)}"
|
|
user_logger.error(error)
|
|
if request.is_json:
|
|
return jsonify({"error": error}), 500
|
|
else:
|
|
flash(error, "error")
|
|
return redirect(url_for("user.profile"))
|
|
finally:
|
|
db_session.close()
|
|
|
|
@user_bp.route("/export", methods=["GET"])
|
|
@login_required
|
|
def export_user_data():
|
|
"""Exportiert alle Benutzerdaten als JSON für DSGVO-Konformität"""
|
|
try:
|
|
db_session = get_db_session()
|
|
user = db_session.query(User).filter(User.id == current_user.id).first()
|
|
|
|
if not user:
|
|
db_session.close()
|
|
return jsonify({"error": "Benutzer nicht gefunden"}), 404
|
|
|
|
# Benutzerdaten abrufen
|
|
from sqlalchemy.orm import joinedload
|
|
user_data = user.to_dict()
|
|
|
|
# Jobs des Benutzers abrufen
|
|
from models import Job
|
|
jobs = db_session.query(Job).filter(Job.user_id == user.id).all()
|
|
user_data["jobs"] = [job.to_dict() for job in jobs]
|
|
|
|
# Aktivitäten und Einstellungen hinzufügen
|
|
from flask import session
|
|
user_data["settings"] = session.get('user_settings', {})
|
|
|
|
# Persönliche Statistiken
|
|
user_data["statistics"] = {
|
|
"total_jobs": len(jobs),
|
|
"completed_jobs": len([j for j in jobs if j.status == "finished"]),
|
|
"failed_jobs": len([j for j in jobs if j.status == "failed"]),
|
|
"account_created": user.created_at.isoformat() if user.created_at else None,
|
|
"last_login": user.last_login.isoformat() if user.last_login else None
|
|
}
|
|
|
|
db_session.close()
|
|
|
|
# Daten als JSON-Datei zum Download anbieten
|
|
from flask import make_response
|
|
import json
|
|
|
|
response = make_response(json.dumps(user_data, indent=4))
|
|
response.headers["Content-Disposition"] = f"attachment; filename=user_data_{user.username}.json"
|
|
response.headers["Content-Type"] = "application/json"
|
|
|
|
user_logger.info(f"Benutzer {current_user.username} hat seine Daten exportiert")
|
|
return response
|
|
|
|
except Exception as e:
|
|
error = f"Fehler beim Exportieren der Benutzerdaten: {str(e)}"
|
|
user_logger.error(error)
|
|
return jsonify({"error": error}), 500
|
|
|
|
@user_bp.route("/profile", methods=["PUT"])
|
|
@login_required
|
|
def update_profile_api():
|
|
"""API-Endpunkt zum Aktualisieren des Benutzerprofils"""
|
|
try:
|
|
if not request.is_json:
|
|
return jsonify({"error": "Anfrage muss im JSON-Format sein"}), 400
|
|
|
|
data = request.get_json()
|
|
db_session = get_db_session()
|
|
user = db_session.query(User).filter(User.id == current_user.id).first()
|
|
|
|
if not user:
|
|
db_session.close()
|
|
return jsonify({"error": "Benutzer nicht gefunden"}), 404
|
|
|
|
# Aktualisiere nur die bereitgestellten Felder
|
|
if "name" in data:
|
|
user.name = data["name"]
|
|
if "email" in data:
|
|
user.email = data["email"]
|
|
if "department" in data:
|
|
user.department = data["department"]
|
|
if "position" in data:
|
|
user.position = data["position"]
|
|
if "phone" in data:
|
|
user.phone = data["phone"]
|
|
if "bio" in data:
|
|
user.bio = data["bio"]
|
|
|
|
user.updated_at = datetime.now()
|
|
db_session.commit()
|
|
|
|
# Aktualisierte Benutzerdaten zurückgeben
|
|
user_data = user.to_dict()
|
|
db_session.close()
|
|
|
|
user_logger.info(f"Benutzer {current_user.username} hat sein Profil über die API aktualisiert")
|
|
return jsonify({
|
|
"success": True,
|
|
"message": "Profil erfolgreich aktualisiert",
|
|
"user": user_data
|
|
})
|
|
|
|
except Exception as e:
|
|
error = f"Fehler beim Aktualisieren des Profils: {str(e)}"
|
|
user_logger.error(error)
|
|
return jsonify({"error": error}), 500 |