Initial release: DictIA v0.8.14-alpha (fork de Speakr, AGPL-3.0)
This commit is contained in:
65
src/utils/security.py
Normal file
65
src/utils/security.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user