ea90a3c9e3
Aenderungen: - E-Mail-Feld im Reservierungs-Formular hinzugefuegt (Pflichtfeld) - Math-Captcha durch Bild-Captcha (4 Zeichen) ersetzt - E-Mail-Validierung im Backend - Captcha-Validierung fuer Reservierungen - Pillow zu Dockerfile hinzugefuegt
101 lines
3.2 KiB
Python
101 lines
3.2 KiB
Python
import os
|
|
import hashlib
|
|
import random
|
|
import time
|
|
import io
|
|
import base64
|
|
from functools import wraps
|
|
from flask import session, request, jsonify
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
ADMIN_PASSWORD_PLAIN = 'changeme'
|
|
SECRET_KEY = os.environ.get('SESSION_SECRET', 'dev-secret-change-in-production')
|
|
|
|
def generate_captcha():
|
|
"""Generiert ein Bild-Captcha mit 4 Zeichen (Zahlen und Buchstaben)"""
|
|
# 4 zufaellige Zeichen (ohne aehnlich aussehende wie O/0, I/l)
|
|
chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
|
|
code = ''.join(random.choice(chars) for _ in range(4))
|
|
|
|
# Token fuer Validierung
|
|
token = hashlib.sha256(f"{code}{int(time.time()/600)}captcha".encode()).hexdigest()[:16]
|
|
|
|
# Antwort in Session speichern
|
|
session['captcha_answer'] = code
|
|
session['captcha_token'] = token
|
|
|
|
# Bild generieren
|
|
img = Image.new('RGB', (180, 60), color='#f8fafc')
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Hintergrund-Noise (Punkte)
|
|
for _ in range(100):
|
|
x = random.randint(0, 180)
|
|
y = random.randint(0, 60)
|
|
draw.point((x, y), fill='#cbd5e1')
|
|
|
|
# Linien hinzufuegen
|
|
for _ in range(5):
|
|
x1 = random.randint(0, 180)
|
|
y1 = random.randint(0, 60)
|
|
x2 = random.randint(0, 180)
|
|
y2 = random.randint(0, 60)
|
|
draw.line([(x1, y1), (x2, y2)], fill='#94a3b8', width=1)
|
|
|
|
# Text zeichnen (mit leichter Rotation pro Buchstabe)
|
|
try:
|
|
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 36)
|
|
except:
|
|
font = ImageFont.load_default()
|
|
|
|
positions = [20, 55, 90, 125]
|
|
for i, char in enumerate(code):
|
|
x = positions[i]
|
|
y = random.randint(12, 18)
|
|
# Zufaellige Farbe pro Buchstabe
|
|
color = random.choice(['#1e40af', '#166534', '#9a3412', '#701a75'])
|
|
draw.text((x, y), char, font=font, fill=color)
|
|
|
|
# Mehr Noise ueber den Text
|
|
for _ in range(50):
|
|
x = random.randint(0, 180)
|
|
y = random.randint(0, 60)
|
|
draw.point((x, y), fill='#64748b')
|
|
|
|
# In Base64 konvertieren
|
|
buffer = io.BytesIO()
|
|
img.save(buffer, format='PNG')
|
|
img_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
|
|
|
|
return {
|
|
"image": f"data:image/png;base64,{img_base64}",
|
|
"token": token
|
|
}
|
|
|
|
def verify_captcha(token, answer):
|
|
"""Ueberprueft die Captcha-Antwort"""
|
|
if not token or not answer:
|
|
return False
|
|
stored = session.get('captcha_answer')
|
|
stored_token = session.get('captcha_token')
|
|
if stored and stored_token == token and str(stored).upper() == str(answer).upper():
|
|
session.pop('captcha_answer', None)
|
|
session.pop('captcha_token', 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):
|
|
return password == ADMIN_PASSWORD_PLAIN
|