v2.1: Security - Captcha, Admin-Login, Auth-Decorator
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python3
|
||||
import re
|
||||
|
||||
with open('/root/reservation-system/app/main.py', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Add auth import
|
||||
old_import = "from database import get_db, init_db, generate_booking_number, log_change"
|
||||
new_import = """from database import get_db, init_db, generate_booking_number, log_change
|
||||
from auth import require_auth, verify_captcha
|
||||
from login_routes import auth_bp"""
|
||||
content = content.replace(old_import, new_import)
|
||||
|
||||
# Add secret key config after app creation
|
||||
old_cors = "CORS(app)"
|
||||
new_cors = """CORS(app)
|
||||
|
||||
# Security config
|
||||
app.secret_key = os.environ.get('SESSION_SECRET', 'dev-secret-change-in-production')
|
||||
|
||||
# Register auth blueprint
|
||||
app.register_blueprint(auth_bp)
|
||||
|
||||
@app.before_request
|
||||
def check_auth():
|
||||
# Public endpoints don't require auth
|
||||
public_endpoints = ['/', '/api/captcha', '/api/rooms', '/api/availability',
|
||||
'/api/health', '/api/admin/login']
|
||||
if request.path in public_endpoints or request.path.startswith('/static/'):
|
||||
return None
|
||||
|
||||
# Admin endpoints require login
|
||||
if request.path.startswith('/api/admin/') and session.get('user_role') != 'admin':
|
||||
return jsonify({"error": "Unauthorized"}), 401"""
|
||||
content = content.replace(old_cors, new_cors)
|
||||
|
||||
# Protect POST reservations with captcha
|
||||
old_post = ''' if request.method == 'POST':
|
||||
data = request.get_json()'''
|
||||
new_post = ''' if request.method == 'POST':
|
||||
data = request.get_json()
|
||||
|
||||
# Captcha for non-admin bookings
|
||||
if session.get('user_role') != 'admin':
|
||||
if not verify_captcha(data.get('captcha_token'), data.get('captcha_answer')):
|
||||
return jsonify({"error": "Invalid captcha. Please solve the math problem."}), 403'''
|
||||
content = content.replace(old_post, new_post, 1)
|
||||
|
||||
with open('/root/reservation-system/app/main.py', 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
print("Security patches applied!")
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import os
|
||||
import hashlib
|
||||
import random
|
||||
import time
|
||||
from functools import wraps
|
||||
from flask import session, request, jsonify
|
||||
|
||||
# Config
|
||||
ADMIN_PASSWORD_HASH = os.environ.get('ADMIN_PASSWORD', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi')
|
||||
SECRET_KEY = os.environ.get('SESSION_SECRET', 'dev-secret-change-in-production')
|
||||
|
||||
def generate_captcha():
|
||||
a = random.randint(1, 15)
|
||||
b = random.randint(1, 15)
|
||||
op = random.choice(['+', '-'])
|
||||
if op == '+':
|
||||
answer = a + b
|
||||
else:
|
||||
answer = a - b
|
||||
token = hashlib.sha256(f"{a}{op}{b}{int(time.time()/600)}captcha".encode()).hexdigest()[:16]
|
||||
return {
|
||||
"question": f"{a} {op} {b} = ?",
|
||||
"token": token,
|
||||
"answer": answer
|
||||
}
|
||||
|
||||
def verify_captcha(token, answer):
|
||||
if not token or not answer:
|
||||
return False
|
||||
stored = session.get('captcha_answer')
|
||||
if stored and str(stored) == str(answer):
|
||||
session.pop('captcha_answer', None)
|
||||
return True
|
||||
return False
|
||||
|
||||
def require_auth(role='admin'):
|
||||
def decorator(f):
|
||||
@wraps(f)
|
||||
def decorated(*args, **kwargs):
|
||||
if 'user_role' not in session:
|
||||
return jsonify({"error": "Unauthorized"}), 401
|
||||
if role == 'admin' and session['user_role'] != 'admin':
|
||||
return jsonify({"error": "Admin required"}), 403
|
||||
return f(*args, **kwargs)
|
||||
return decorated
|
||||
return decorator
|
||||
|
||||
def check_admin_password(password):
|
||||
from werkzeug.security import check_password_hash
|
||||
return check_password_hash(ADMIN_PASSWORD_HASH, password)
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Login und Captcha Routes"""
|
||||
from flask import Blueprint, request, jsonify, session
|
||||
from auth import generate_captcha, verify_captcha, check_admin_password
|
||||
|
||||
auth_bp = Blueprint('auth', __name__)
|
||||
|
||||
@auth_bp.route('/api/captcha', methods=['GET'])
|
||||
def get_captcha():
|
||||
captcha = generate_captcha()
|
||||
session['captcha_answer'] = captcha['answer']
|
||||
return jsonify({"question": captcha['question'], "token": captcha['token']})
|
||||
|
||||
@auth_bp.route('/api/admin/login', methods=['POST'])
|
||||
def admin_login():
|
||||
data = request.get_json() or {}
|
||||
password = data.get('password', '')
|
||||
|
||||
if check_admin_password(password):
|
||||
session['user_role'] = 'admin'
|
||||
session['login_time'] = __import__('time').time()
|
||||
return jsonify({"status": "ok", "role": "admin"})
|
||||
return jsonify({"error": "Invalid credentials"}), 401
|
||||
|
||||
@auth_bp.route('/api/admin/logout', methods=['POST'])
|
||||
def admin_logout():
|
||||
session.pop('user_role', None)
|
||||
session.pop('login_time', None)
|
||||
return jsonify({"status": "ok"})
|
||||
|
||||
@auth_bp.route('/api/session', methods=['GET'])
|
||||
def check_session():
|
||||
role = session.get('user_role')
|
||||
if role:
|
||||
return jsonify({"role": role, "logged_in": True})
|
||||
return jsonify({"role": None, "logged_in": False})
|
||||
+20
@@ -10,6 +10,8 @@ from flask import Flask, request, jsonify, render_template, send_from_directory,
|
||||
from flask_cors import CORS
|
||||
|
||||
from database import get_db, init_db, generate_booking_number, log_change
|
||||
from auth import require_auth, verify_captcha
|
||||
from login_routes import auth_bp
|
||||
from utils.ollama_client import (
|
||||
parse_email_with_ollama,
|
||||
generate_confirmation_email,
|
||||
@@ -21,6 +23,24 @@ app = Flask(__name__,
|
||||
static_folder='static')
|
||||
CORS(app)
|
||||
|
||||
# Security config
|
||||
app.secret_key = os.environ.get('SESSION_SECRET', 'dev-secret-change-in-production')
|
||||
|
||||
# Register auth blueprint
|
||||
app.register_blueprint(auth_bp)
|
||||
|
||||
@app.before_request
|
||||
def check_auth():
|
||||
# Public endpoints don't require auth
|
||||
public_endpoints = ['/', '/api/captcha', '/api/rooms', '/api/availability',
|
||||
'/api/health', '/api/admin/login']
|
||||
if request.path in public_endpoints or request.path.startswith('/static/'):
|
||||
return None
|
||||
|
||||
# Admin endpoints require login
|
||||
if request.path.startswith('/api/admin/') and session.get('user_role') != 'admin':
|
||||
return jsonify({"error": "Unauthorized"}), 401
|
||||
|
||||
# Konfiguration
|
||||
DEFAULT_OPEN_HOUR = 10 # 10:00
|
||||
DEFAULT_CLOSE_HOUR = 23 # 23:00
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user