From e8ea8138969ed04f3bf9ccd98864f59ec45bf736 Mon Sep 17 00:00:00 2001 From: nocci Date: Sun, 4 May 2025 17:30:25 +0200 Subject: [PATCH] minor changes due problems deleting users and generating new passwords --- setup.sh | 118 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 33 deletions(-) diff --git a/setup.sh b/setup.sh index dda5de0..ee3d8e7 100644 --- a/setup.sh +++ b/setup.sh @@ -116,6 +116,8 @@ gunicorn apprise debugpy pytz +Flask-Session +redis EOL # 3. .env Datei in Parent-Folder @@ -155,6 +157,9 @@ APPRISE_URLS="" #gotify://gotify.example.com/TOKEN #matrixs://TOKEN@matrix.org/!ROOM_ID" +# Redis URL +REDIS_URL=redis://redis:6379/0 + # Enable Debug (e.g. for VS Code) DEBUGPY=0 EOL @@ -186,12 +191,14 @@ from flask import ( abort ) from flask_sqlalchemy import SQLAlchemy +from flask_session import Session from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user from werkzeug.security import generate_password_hash, check_password_hash from flask_wtf import CSRFProtect from flask import abort from flask import request, redirect from flask_wtf import FlaskForm +from flask_wtf.csrf import CSRFProtect from wtforms import StringField, SelectField, TextAreaField, validators import io import warnings @@ -232,6 +239,8 @@ from sqlalchemy.engine import Engine import sqlite3 from sqlalchemy.orm import joinedload from functools import wraps +from flask_session import Session +from redis import Redis @event.listens_for(Engine, "connect") def enable_foreign_keys(dbapi_connection, connection_record): @@ -287,7 +296,9 @@ if app.debug: def admin_required(f): @wraps(f) def decorated_function(*args, **kwargs): - if not current_user.is_authenticated or not getattr(current_user, 'is_admin', False): + if not current_user.is_authenticated: + abort(403) + if not current_user.is_admin: abort(403) return f(*args, **kwargs) return decorated_function @@ -311,20 +322,38 @@ load_dotenv(override=True) # App-Configuration app.config.update( + # WICHTIGSTE EINSTELLUNGEN SECRET_KEY=os.getenv('SECRET_KEY'), - SQLALCHEMY_DATABASE_URI='sqlite:////app/data/games.db', - SQLALCHEMY_TRACK_MODIFICATIONS=False, + SQLALCHEMY_DATABASE_URI = 'sqlite:////app/data/games.db', + SQLALCHEMY_TRACK_MODIFICATIONS = False, + + # SESSION-HANDLING (Produktion: Redis verwenden!) + SESSION_TYPE='redis', + SESSION_PERMANENT = False, + SESSION_USE_SIGNER = True, + SESSION_REDIS=Redis.from_url(os.getenv("REDIS_URL", "redis://redis:6379/0")), + SESSION_FILE_DIR = '/app/data/flask-sessions', SESSION_COOKIE_NAME = 'gamekeys_session', - SESSION_COOKIE_SECURE=os.getenv('SESSION_COOKIE_SECURE', 'False') == 'True', - SESSION_COOKIE_SAMESITE='Lax', - PERMANENT_SESSION_LIFETIME=timedelta(days=30), - SESSION_REFRESH_EACH_REQUEST=False, - WTF_CSRF_ENABLED=os.getenv('CSRF_ENABLED', 'True') == 'True', - REGISTRATION_ENABLED=os.getenv('REGISTRATION_ENABLED', 'True').lower() == 'true', - SEND_FILE_MAX_AGE_DEFAULT=int(os.getenv('SEND_FILE_MAX_AGE_DEFAULT', 0)), - TEMPLATES_AUTO_RELOAD=os.getenv('TEMPLATES_AUTO_RELOAD', 'True') == 'True' + SESSION_COOKIE_SECURE = os.getenv('SESSION_COOKIE_SECURE', 'False').lower() == 'true', + SESSION_COOKIE_HTTPONLY = True, + SESSION_COOKIE_SAMESITE = 'Lax', + PERMANENT_SESSION_LIFETIME = timedelta(days=30), + + # CSRF-PROTECTION + WTF_CSRF_ENABLED = True, + WTF_CSRF_SECRET_KEY = os.getenv('CSRF_SECRET_KEY', os.urandom(32).hex()), + WTF_CSRF_TIME_LIMIT = 3600, + + # SECURITYsa & PERFORMANCE + REGISTRATION_ENABLED = os.getenv('REGISTRATION_ENABLED', 'True').lower() == 'true', + SEND_FILE_MAX_AGE_DEFAULT = int(os.getenv('SEND_FILE_MAX_AGE_DEFAULT', 0)), + TEMPLATES_AUTO_RELOAD = os.getenv('TEMPLATES_AUTO_RELOAD', 'True').lower() == 'true', + PREFERRED_URL_SCHEME = 'https' if os.getenv('FORCE_HTTPS') else 'http' ) + +Session(app) + interval_hours = int(os.getenv('CHECK_EXPIRING_KEYS_INTERVAL_HOURS', 12)) # Initialisation @@ -993,8 +1022,13 @@ def admin_reset_password(user_id): new_password = secrets.token_urlsafe(8) user.password = generate_password_hash(new_password) db.session.commit() - flash(translate('New password for %(username)s: %(password)s', - username=user.username, password=new_password), 'info') + + flash( + translate('New password for {username}: {password}', + username=user.username, + password=new_password), + 'info' + ) return redirect(url_for('admin_users')) @@ -1922,9 +1956,9 @@ cat < templates/admin_users.html -
+ - +
{% endif %} @@ -2103,6 +2137,7 @@ chmod +x entrypoint.sh # create translate.sh and run it cat <<'SCRIPT_END' > ../translate.sh +#!/bin/bash set -e APP_DIR="steam-gift-manager" @@ -2114,35 +2149,32 @@ if ! command -v jq &>/dev/null; then echo "❌ jq is required. Install with: sudo apt-get install jq" exit 1 fi -echo -e "\n\033[1;32m✅ we are extracting translations from the templates.\033[0m" -# 1. Lege JSON-Dateien an, falls sie fehlen + +echo -e "\n\033[1;32m✅ Extracting translations...\033[0m" + +# 1. create json files +mkdir -p "$TRANSLATION_DIR" for lang in "${LANGS[@]}"; do file="$TRANSLATION_DIR/$lang.json" - if [ ! -f "$file" ]; then - echo "{}" > "$file" - echo "Created $file" - fi + [ -f "$file" ] || echo "{}" > "$file" done -# 2. Extrahiere alle zu übersetzenden Strings -STRINGS=$(grep -rhoP "_\(\s*['\"](.+?)['\"]\s*\)" \ +# 2. extract all strings +STRINGS=$(grep -rhoP "_\(\s*['\"]((?:[^']|'[^'])*?)['\"]\s*[,)]" \ "$APP_DIR/templates" "$APP_DIR/app.py" | \ - sed -E "s/_\(\s*['\"](.+?)['\"]\s*\)/\1/" | sort | uniq) + sed -E "s/_\(\s*['\"](.+?)['\"]\s*[,)]/\1/" | sort | uniq) -# 3. Ergänze neue Keys in die JSON-Dateien +# 3. put da keys in da json for lang in "${LANGS[@]}"; do file="$TRANSLATION_DIR/$lang.json" tmp="$file.tmp" - cp "$file" "$tmp" - while IFS= read -r key; do - if ! jq -e --arg k "$key" 'has($k)' "$tmp" >/dev/null; then - jq --arg k "$key" '. + {($k): ""}' "$tmp" > "$tmp.new" && mv "$tmp.new" "$tmp" - fi - done <<< "$STRINGS" + jq --argjson keys "$(echo "$STRINGS" | jq -R . | jq -s .)" \ + 'reduce $keys[] as $k (.; .[$k] = (.[$k] // ""))' "$file" > "$tmp" mv "$tmp" "$file" - echo "Updated $file" done -echo -e "\n\033[1;32m✅ JSON translation files updated. Please enter your translations!\033[0m" + +echo -e "\n\033[1;32m✅ Done! Translation keys added.\033[0m" + SCRIPT_END chmod +x ../translate.sh @@ -2201,6 +2233,15 @@ DOCKER_END # 6. docker-compose.yml cat < docker-compose.yml services: + redis: + image: redis:alpine + ports: + - "6379:6379" + volumes: + - redis_data:/data + networks: + - app-network + steam-manager: build: context: . @@ -2214,6 +2255,7 @@ services: - FLASK_DEBUG=0 - REGISTRATION_ENABLED=${REGISTRATION_ENABLED:-True} - TZ=${TZ} + - REDIS_URL=redis://redis:6379/0 volumes: - ../data:/app/data - ./translations:/app/translations:rw @@ -2221,6 +2263,16 @@ services: user: "${UID:-1000}:${GID:-1000}" restart: unless-stopped command: ["/app/entrypoint.sh"] + networks: + - app-network + depends_on: + - redis +volumes: + redis_data: + +networks: + app-network: + driver: bridge COMPOSE_END