Files
digiserver-v2/app/blueprints/auth.py
Quality App Developer 48f1bfbcad Add HTTPS configuration management system
- Add HTTPSConfig model for managing HTTPS settings
- Add admin routes for HTTPS configuration management
- Add beautiful admin template for HTTPS configuration
- Add database migration for https_config table
- Add CLI utility for HTTPS management
- Add setup script for automated configuration
- Add Caddy configuration generator and manager
- Add comprehensive documentation (3 guides)
- Add HTTPS Configuration card to admin dashboard
- Implement input validation and security features
- Add admin-only access control with audit trail
- Add real-time configuration preview
- Integrate with existing Caddy reverse proxy

Features:
- Enable/disable HTTPS from web interface
- Configure domain, hostname, IP address, port
- Automatic SSL certificate management via Let's Encrypt
- Real-time Caddyfile generation and reload
- Full audit trail with admin username and timestamps
- Support for HTTPS and HTTP fallback access points
- Beautiful, mobile-responsive UI

Modified files:
- app/models/__init__.py (added HTTPSConfig import)
- app/blueprints/admin.py (added HTTPS routes)
- app/templates/admin/admin.html (added HTTPS card)
- docker-compose.yml (added Caddyfile mount and admin port)

New files:
- app/models/https_config.py
- app/blueprints/https_config.html
- app/utils/caddy_manager.py
- https_manager.py
- setup_https.sh
- migrations/add_https_config_table.py
- migrations/add_email_to_https_config.py
- HTTPS_STATUS.txt
- Documentation files (3 markdown guides)
2026-01-14 12:02:49 +02:00

148 lines
5.3 KiB
Python
Executable File

"""
Authentication Blueprint - Login, Logout, Register
"""
from flask import Blueprint, render_template, request, redirect, url_for, flash
from flask_login import login_user, logout_user, login_required, current_user
from app.extensions import db, bcrypt, login_manager
from app.models import User
from app.utils.logger import log_action
from typing import Optional
auth_bp = Blueprint('auth', __name__)
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
"""User login"""
# Redirect if already logged in
if current_user.is_authenticated:
return redirect(url_for('main.dashboard'))
if request.method == 'POST':
username = request.form.get('username', '').strip()
password = request.form.get('password', '')
remember = request.form.get('remember', False)
# Validate input
if not username or not password:
flash('Please provide both username and password.', 'danger')
return render_template('auth/login.html')
# Find user
user = User.query.filter_by(username=username).first()
# Verify credentials
if user and bcrypt.check_password_hash(user.password, password):
login_user(user, remember=remember)
log_action('info', f'User {username} logged in')
# Redirect to next page or dashboard
next_page = request.args.get('next')
if next_page:
return redirect(next_page)
return redirect(url_for('main.dashboard'))
else:
flash('Invalid username or password.', 'danger')
log_action('warning', f'Failed login attempt for username: {username}')
# Check for logo
import os
login_picture_exists = os.path.exists(
os.path.join(auth_bp.root_path or '.', 'static/resurse/login_picture.png')
)
return render_template('auth/login.html', login_picture_exists=login_picture_exists)
@auth_bp.route('/logout')
@login_required
def logout():
"""User logout"""
username = current_user.username
logout_user()
log_action('info', f'User {username} logged out')
flash('You have been logged out.', 'info')
return redirect(url_for('auth.login'))
@auth_bp.route('/register', methods=['GET', 'POST'])
def register():
"""User registration"""
# Redirect if already logged in
if current_user.is_authenticated:
return redirect(url_for('main.dashboard'))
if request.method == 'POST':
username = request.form.get('username', '').strip()
password = request.form.get('password', '')
confirm_password = request.form.get('confirm_password', '')
# Validate input
if not username or not password:
flash('Username and password are required.', 'danger')
return render_template('auth/register.html')
if password != confirm_password:
flash('Passwords do not match.', 'danger')
return render_template('auth/register.html')
if len(password) < 6:
flash('Password must be at least 6 characters long.', 'danger')
return render_template('auth/register.html')
# Check if user already exists
if User.query.filter_by(username=username).first():
flash('Username already exists.', 'danger')
return render_template('auth/register.html')
# Create new user
hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
new_user = User(
username=username,
password=hashed_password,
role='viewer' # Default role
)
db.session.add(new_user)
db.session.commit()
log_user_created(username, 'viewer')
flash('Registration successful! Please log in.', 'success')
return redirect(url_for('auth.login'))
return render_template('auth/register.html')
@auth_bp.route('/change-password', methods=['GET', 'POST'])
@login_required
def change_password():
"""Change user password"""
if request.method == 'POST':
current_password = request.form.get('current_password', '')
new_password = request.form.get('new_password', '')
confirm_password = request.form.get('confirm_password', '')
# Verify current password
if not bcrypt.check_password_hash(current_user.password, current_password):
flash('Current password is incorrect.', 'danger')
return render_template('auth/change_password.html')
# Validate new password
if new_password != confirm_password:
flash('New passwords do not match.', 'danger')
return render_template('auth/change_password.html')
if len(new_password) < 6:
flash('Password must be at least 6 characters long.', 'danger')
return render_template('auth/change_password.html')
# Update password
current_user.password = bcrypt.generate_password_hash(new_password).decode('utf-8')
db.session.commit()
log_action('info', f'User {current_user.username} changed password')
flash('Password changed successfully.', 'success')
return redirect(url_for('main.dashboard'))
return render_template('auth/change_password.html')