""" Naue Roll Calculator - Beta Flask app with HTTP Basic Authentication """ from flask import Flask, render_template, request, send_file, send_from_directory, jsonify from flask_httpauth import HTTPBasicAuth from functools import wraps import os from datetime import datetime import json app = Flask(__name__) auth = HTTPBasicAuth() # ============================================================================ # CONFIGURATION # ============================================================================ # Benutzer für Beta-Phase (in Produktion aus env-Variablen laden!) BETA_USERS = { "beta": "rollcalc_beta_2026", # ⚠️ ÄNDERN! "naue": "naue_access_2026", # ⚠️ ÄNDERN! "cniehues": "beta_test_2026", # ⚠️ ÄNDERN! "lvollmert": "Geheim!", # ⚠️ ÄNDERN! "mtazl": "rollcalc", # ⚠️ ÄNDERN! "controlling": "beta2026" # ⚠️ ÄNDERN! } # Optional: Admin-Features (z.B. Logs, Stats) ADMIN_USERS = { "admin": "admin_secure_pwd_2026" # ⚠️ ÄNDERN! } # Logging für Auditing LOG_FILE = "access_log.json" # ============================================================================ # AUTHENTICATION # ============================================================================ @auth.verify_password def verify_password(username, password): """Verify HTTP Basic Auth credentials""" all_users = {**BETA_USERS, **ADMIN_USERS} if username in all_users and all_users[username] == password: return username return None def log_access(username, endpoint, method, status=200): """Log all access attempts for audit trail""" log_entry = { "timestamp": datetime.now().isoformat(), "username": username, "endpoint": endpoint, "method": method, "status": status } try: logs = [] if os.path.exists(LOG_FILE): with open(LOG_FILE, "r") as f: logs = json.load(f) logs.append(log_entry) with open(LOG_FILE, "w") as f: json.dump(logs, f, indent=2) except Exception as e: print(f"[Logging Error] {e}") # ============================================================================ # ROUTES # ============================================================================ @app.route("/", methods=["GET"]) @auth.login_required def index(): """Main calculator page - requires authentication""" log_access(auth.current_user(), "/", "GET") return render_template("roll_calculator.html") @app.route("/static/", methods=["GET"]) @auth.login_required def serve_static(filename): """Serve static files (CSS, JS, JSON) - protected""" log_access(auth.current_user(), f"/static/{filename}", "GET") return send_from_directory("static", filename) @app.route("/api/health", methods=["GET"]) @auth.login_required def health_check(): """Simple health check endpoint""" return jsonify({"status": "ok", "version": "14.1"}), 200 @app.route("/api/user", methods=["GET"]) @auth.login_required def get_user(): """Get current authenticated user info""" user = auth.current_user() is_admin = user in ADMIN_USERS return jsonify({ "username": user, "authenticated": True, "is_admin": is_admin, "timestamp": datetime.now().isoformat() }), 200 @app.route("/admin/logs", methods=["GET"]) @auth.login_required def get_logs(): """View access logs - admin only""" user = auth.current_user() if user not in ADMIN_USERS: log_access(user, "/admin/logs", "GET", 403) return jsonify({"error": "Unauthorized"}), 403 log_access(user, "/admin/logs", "GET") logs = [] if os.path.exists(LOG_FILE): with open(LOG_FILE, "r") as f: logs = json.load(f) return jsonify({"logs": logs}), 200 # ============================================================================ # ERROR HANDLERS # ============================================================================ @app.errorhandler(401) def unauthorized(e): """Handle 401 Unauthorized - browser will prompt for credentials""" return jsonify({"error": "Unauthorized - please provide valid credentials"}), 401 @app.errorhandler(404) def not_found(e): """Handle 404 Not Found""" return jsonify({"error": "Not found"}), 404 @app.errorhandler(500) def internal_error(e): """Handle 500 Internal Server Error""" return jsonify({"error": "Internal server error"}), 500 # ============================================================================ # STARTUP # ============================================================================ if __name__ == "__main__": os.makedirs("templates", exist_ok=True) os.makedirs("static", exist_ok=True) print(""" ╔═══════════════════════════════════════════════════════════╗ ║ Naue Roll Calculator - V14 + QoL Update ║ ║ HTTP Basic Auth Enabled ║ ║ ║ ║ ⚠️ BEFORE PRODUCTION: Update passwords in app.py ║ ║ ║ ║ Starten auf: http://localhost:5000 ║ ╚═══════════════════════════════════════════════════════════╝ """) app.run( host="127.0.0.1", port=5000, debug=False, use_reloader=False )