66 lines
2.1 KiB
Python
66 lines
2.1 KiB
Python
"""
|
|
Security utilities for password validation and other security functions.
|
|
|
|
This module provides security-related utility functions for the application.
|
|
"""
|
|
|
|
import re
|
|
from functools import wraps
|
|
from wtforms.validators import ValidationError
|
|
from urllib.parse import urlparse, urljoin
|
|
from flask import request, jsonify
|
|
from flask_login import login_required, current_user
|
|
|
|
|
|
def password_check(form, field):
|
|
"""
|
|
Custom WTForms validator for password strength.
|
|
|
|
Validates that passwords meet security requirements:
|
|
- At least 8 characters long
|
|
- Contains at least one uppercase letter
|
|
- Contains at least one lowercase letter
|
|
- Contains at least one number
|
|
- Contains at least one special character
|
|
|
|
Args:
|
|
form: WTForms form object
|
|
field: WTForms field object containing the password
|
|
|
|
Raises:
|
|
ValidationError: If password doesn't meet requirements
|
|
"""
|
|
password = field.data
|
|
if len(password) < 8:
|
|
raise ValidationError('Password must be at least 8 characters long.')
|
|
if not re.search(r'[A-Z]', password):
|
|
raise ValidationError('Password must contain at least one uppercase letter.')
|
|
if not re.search(r'[a-z]', password):
|
|
raise ValidationError('Password must contain at least one lowercase letter.')
|
|
if not re.search(r'[0-9]', password):
|
|
raise ValidationError('Password must contain at least one number.')
|
|
if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
|
|
raise ValidationError('Password must contain at least one special character.')
|
|
|
|
|
|
# --- Access control decorators ---
|
|
|
|
def admin_required(f):
|
|
"""Decorator that requires the current user to be authenticated and an admin."""
|
|
@wraps(f)
|
|
@login_required
|
|
def decorated(*args, **kwargs):
|
|
if not current_user.is_admin:
|
|
return jsonify({'error': 'Admin access required'}), 403
|
|
return f(*args, **kwargs)
|
|
return decorated
|
|
|
|
|
|
# --- URL Security ---
|
|
|
|
def is_safe_url(target):
|
|
ref_url = urlparse(request.host_url)
|
|
test_url = urlparse(urljoin(request.host_url, target))
|
|
return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc
|
|
|