This commit is contained in:
2025-11-18 17:00:51 -05:00
8 changed files with 329 additions and 124 deletions
+18 -124
View File
@@ -27,6 +27,24 @@ from turnstile_utils import turnstile_utils
from db_performance_optimization import initialize_performance_optimizations
from app_performance_middleware import PerformanceMonitor
from utils.validation import (
is_valid_role,
has_admin_privileges,
has_staff_level_access,
has_export_permissions,
get_role_permissions,
VALID_ROLES,
STAFF_LEVEL_ROLES
)
from utils.auth_decorators import (
login_required,
admin_required,
staff_or_admin_required,
project_manager_required,
payroll_required
)
# Initialize Flask application
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
@@ -211,12 +229,6 @@ def cache_coordinates(address, lat, lng, accuracy):
except Exception as e:
print(f"⚠️ Error caching coordinates: {e}")
# Valid user roles with new additions
VALID_ROLES = ['admin', 'staff', 'payroll', 'project_manager']
# Roles that have staff-level permissions (non-admin roles)
STAFF_LEVEL_ROLES = ['staff', 'payroll', 'project_manager']
# Import and initialize models
from models import set_db
User, QRCode, QRCodeStyle, Project, AttendanceData, Employee, TimeAttendance, UserProjectPermission, UserLocationPermission = set_db(db)
@@ -224,82 +236,7 @@ User, QRCode, QRCodeStyle, Project, AttendanceData, Employee, TimeAttendance, Us
# Initialize the logging system
logger_handler = AppLogger(app, db)
# Utility functions
def is_valid_role(role):
"""Check if role is valid"""
return role in VALID_ROLES
def has_admin_privileges(role):
"""Check if role has admin privileges"""
return role == 'admin'
def has_staff_level_access(role):
"""Check if role has staff-level access (includes new roles)"""
return role in STAFF_LEVEL_ROLES
def get_role_permissions(role):
"""Get permissions description for a role"""
permissions = {
'admin': {
'title': 'Administrator Permissions',
'permissions': [
'Full QR code management (create, edit, delete)',
'Complete user management capabilities',
'System configuration access',
'View all system analytics',
'Bulk operations and data export',
'Access to all admin features'
],
'restrictions': ['With great power comes great responsibility!']
},
'staff': {
'title': 'Staff User Permissions',
'permissions': [
'Create and edit QR codes',
'View all QR codes in the system',
'Download QR code images',
'Update personal profile information',
],
'restrictions': [
'Cannot delete QR codes',
'Cannot manage other users',
'Cannot access admin settings'
]
},
'payroll': {
'title': 'Payroll Specialist Permissions',
'permissions': [
'Create and edit QR codes',
'View all QR codes in the system',
'Download QR code images',
'Update personal profile information',
'Access dashboard and reports',
'Same permissions as Staff (additional features coming soon)'
],
'restrictions': [
'Cannot delete QR codes',
'Cannot manage other users',
'Cannot access admin settings'
]
},
'project_manager': {
'title': 'Project Manager Permissions',
'permissions': [
'Create and edit QR codes',
'View all QR codes in the system',
'Download QR code images',
'Update personal profile information',
'Access dashboard and reports',
'Same permissions as Staff (additional features coming soon)'
],
'restrictions': [
'Cannot delete QR codes',
'Cannot manage other users',
'Cannot access admin settings'
]
}
}
return permissions.get(role, {})
def log_google_maps_usage(operation_type):
"""Log Google Maps API usage for monitoring"""
@@ -1354,49 +1291,6 @@ def format_time_interval(minutes):
else:
return f"{days}d {remaining_hours}h"
# Authentication decorator
def login_required(f):
"""Decorator to ensure user is logged in"""
@wraps(f)
def decorated_function(*args, **kwargs):
if 'user_id' not in session:
flash('Please log in to access this page.', 'error')
return redirect(url_for('login'))
return f(*args, **kwargs)
return decorated_function
def admin_required(f):
"""Decorator to ensure user has admin privileges"""
@wraps(f)
def decorated_function(*args, **kwargs):
if 'username' not in session:
flash('Please log in to access this page.', 'error')
return redirect(url_for('login'))
user_role = session.get('role')
if not has_admin_privileges(user_role):
flash('Administrator privileges required for this action.', 'error')
return redirect(url_for('dashboard'))
return f(*args, **kwargs)
return decorated_function
def staff_or_admin_required(f):
"""Decorator to ensure user has staff-level or admin privileges"""
@wraps(f)
def decorated_function(*args, **kwargs):
if 'username' not in session:
flash('Please log in to access this page.', 'error')
return redirect(url_for('login'))
user_role = session.get('role')
if not (has_admin_privileges(user_role) or has_staff_level_access(user_role)):
flash('Insufficient privileges to access this page.', 'error')
return redirect(url_for('dashboard'))
return f(*args, **kwargs)
return decorated_function
# Add this helper function to check admin requirements more safely
def is_admin_user(user_id):
"""Helper function to safely check if user is admin"""
+54
View File
@@ -0,0 +1,54 @@
"""
Utilities Package for QR Attendance Management System
====================================================
This package provides consolidated utility functions extracted from app.py
for better code organization and reusability.
Modules:
- auth_decorators: Authentication and authorization decorators
- validation: Role validation and permissions checking
- location_utils: Location and geocoding utilities
- qr_utils: QR code generation utilities
- date_time_utils: Date and time formatting utilities
- helpers: General helper functions
"""
# Authentication decorators
from .auth_decorators import (
login_required,
admin_required,
staff_or_admin_required,
project_manager_required,
payroll_required
)
# Validation functions
from .validation import (
is_valid_role,
has_admin_privileges,
has_staff_level_access,
has_export_permissions,
get_role_permissions,
VALID_ROLES,
STAFF_LEVEL_ROLES
)
# Export all for easy importing
__all__ = [
# Decorators
'login_required',
'admin_required',
'staff_or_admin_required',
'project_manager_required',
'payroll_required',
# Validation
'is_valid_role',
'has_admin_privileges',
'has_staff_level_access',
'has_export_permissions',
'get_role_permissions',
'VALID_ROLES',
'STAFF_LEVEL_ROLES',
]
+125
View File
@@ -0,0 +1,125 @@
"""
Authentication and Authorization Decorators
==========================================
Consolidated authentication decorators to eliminate duplication.
Extracted from app.py for better code organization.
"""
from functools import wraps
from flask import session, flash, redirect, url_for
from utils.validation import has_admin_privileges, has_staff_level_access
def login_required(f):
"""
Decorator to ensure user is logged in
Usage:
@app.route('/dashboard')
@login_required
def dashboard():
return render_template('dashboard.html')
"""
@wraps(f)
def decorated_function(*args, **kwargs):
if 'user_id' not in session:
flash('Please log in to access this page.', 'error')
return redirect(url_for('login'))
return f(*args, **kwargs)
return decorated_function
def admin_required(f):
"""
Decorator to ensure user has admin privileges
Usage:
@app.route('/admin/users')
@admin_required
def manage_users():
return render_template('users.html')
"""
@wraps(f)
def decorated_function(*args, **kwargs):
if 'username' not in session:
flash('Please log in to access this page.', 'error')
return redirect(url_for('login'))
user_role = session.get('role')
if not has_admin_privileges(user_role):
flash('Administrator privileges required for this action.', 'error')
return redirect(url_for('dashboard'))
return f(*args, **kwargs)
return decorated_function
def staff_or_admin_required(f):
"""
Decorator to ensure user has staff-level or admin privileges
Usage:
@app.route('/attendance/records')
@staff_or_admin_required
def view_records():
return render_template('records.html')
"""
@wraps(f)
def decorated_function(*args, **kwargs):
if 'username' not in session:
flash('Please log in to access this page.', 'error')
return redirect(url_for('login'))
user_role = session.get('role')
if not (has_admin_privileges(user_role) or has_staff_level_access(user_role)):
flash('Insufficient privileges to access this page.', 'error')
return redirect(url_for('dashboard'))
return f(*args, **kwargs)
return decorated_function
def project_manager_required(f):
"""
Decorator to ensure user is project manager or admin
Usage:
@app.route('/projects/dashboard')
@project_manager_required
def project_dashboard():
return render_template('project_dashboard.html')
"""
@wraps(f)
def decorated_function(*args, **kwargs):
if 'username' not in session:
flash('Please log in to access this page.', 'error')
return redirect(url_for('login'))
user_role = session.get('role')
if user_role not in ['admin', 'project_manager']:
flash('Project Manager access required.', 'error')
return redirect(url_for('dashboard'))
return f(*args, **kwargs)
return decorated_function
def payroll_required(f):
"""
Decorator to ensure user is payroll staff or admin
Usage:
@app.route('/payroll/process')
@payroll_required
def process_payroll():
return render_template('payroll.html')
"""
@wraps(f)
def decorated_function(*args, **kwargs):
if 'username' not in session:
flash('Please log in to access this page.', 'error')
return redirect(url_for('login'))
user_role = session.get('role')
if user_role not in ['admin', 'payroll']:
flash('Payroll access required.', 'error')
return redirect(url_for('dashboard'))
return f(*args, **kwargs)
return decorated_function
View File
View File
View File
View File
+132
View File
@@ -0,0 +1,132 @@
"""
Validation Utilities for QR Attendance Management System
========================================================
Role validation and permissions checking functions.
Extracted from app.py for better code organization.
"""
# Valid user roles
VALID_ROLES = ['admin', 'staff', 'payroll', 'project_manager']
STAFF_LEVEL_ROLES = ['staff', 'payroll', 'project_manager']
def is_valid_role(role):
"""
Check if role is valid
Args:
role (str): User role to validate
Returns:
bool: True if role is valid
"""
return role in VALID_ROLES
def has_admin_privileges(role):
"""
Check if role has admin privileges
Args:
role (str): User role to check
Returns:
bool: True if role is admin
"""
return role == 'admin'
def has_staff_level_access(role):
"""
Check if role has staff-level access (includes payroll, project_manager)
Args:
role (str): User role to check
Returns:
bool: True if role has staff-level permissions
"""
return role in STAFF_LEVEL_ROLES
def has_export_permissions(role):
"""
Check if user role has export permissions
Args:
role (str): User role to check
Returns:
bool: True if role can export data
"""
return role in ['admin', 'payroll']
def get_role_permissions(role):
"""
Get permissions description for a role
Args:
role (str): User role
Returns:
dict: Role permissions with title, permissions list, and restrictions
"""
permissions = {
'admin': {
'title': 'Administrator Permissions',
'permissions': [
'Full QR code management (create, edit, delete)',
'Complete user management capabilities',
'System configuration access',
'View all system analytics',
'Bulk operations and data export',
'Access to all admin features'
],
'restrictions': ['With great power comes great responsibility!']
},
'staff': {
'title': 'Staff User Permissions',
'permissions': [
'View QR codes and attendance records',
'Check-in to attendance',
'View own attendance history'
],
'restrictions': [
'Cannot create or modify QR codes',
'Cannot manage users',
'Limited export capabilities'
]
},
'payroll': {
'title': 'Payroll Specialist Permissions',
'permissions': [
'Full payroll processing access',
'Export attendance and payroll data',
'View all attendance records',
'Generate payroll reports'
],
'restrictions': [
'Cannot manage QR codes',
'Cannot manage users',
'Read-only access to configurations'
]
},
'project_manager': {
'title': 'Project Manager Permissions',
'permissions': [
'View assigned project data',
'View assigned location data',
'Generate project reports',
'Monitor project attendance'
],
'restrictions': [
'Access limited to assigned projects/locations',
'Cannot modify QR codes',
'Cannot manage users',
'Read-only access'
]
}
}
return permissions.get(role, {
'title': 'Unknown Role',
'permissions': [],
'restrictions': ['Invalid role specified']
})