Fix superadmin access control and modal aria-hidden warning

- Implement role normalization system to handle role name variants (superadmin, super_admin, administrator)
- Add session persistence configuration (PERMANENT_SESSION_LIFETIME = 7 days)
- Add modules JSON column to users database table schema
- Update setup script with backward compatibility check for modules column
- Fix user_management_simple route to properly fetch and display modules
- Resolve modal aria-hidden accessibility warning by managing focus on close button
- All changes deployed and tested successfully
This commit is contained in:
Quality App Developer
2025-12-26 20:08:54 +02:00
parent 8f6f27722a
commit d09bf34e85
11 changed files with 77 additions and 8719 deletions

View File

@@ -5,6 +5,13 @@ def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'
# Configure session persistence
from datetime import timedelta
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)
app.config['SESSION_COOKIE_SECURE'] = False # Set to True in production with HTTPS
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
# Set max upload size to 10GB for large database backups
app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024 * 1024 # 10GB

View File

@@ -3,7 +3,7 @@ Simple access control decorators for the 4-tier system
"""
from functools import wraps
from flask import session, redirect, url_for, flash, request
from .permissions_simple import check_access, ROLES
from .permissions_simple import check_access, ROLES, normalize_role
def requires_role(min_role_level=None, required_modules=None, page=None):
"""
@@ -22,9 +22,21 @@ def requires_role(min_role_level=None, required_modules=None, page=None):
flash('Please log in to access this page.')
return redirect(url_for('main.login'))
user_role = session.get('role')
user_role_raw = session.get('role')
user_role = normalize_role(user_role_raw)
user_modules = session.get('modules', [])
# Debug - write to a variable we can check
import json
debug_info = {
'user': session.get('user'),
'raw_role': user_role_raw,
'normalized_role': user_role,
'modules': user_modules,
'min_level_needed': min_role_level,
'requested_page': request.path
}
# If page is specified, use automatic access checking
if page:
if not check_access(user_role, user_modules, page):
@@ -35,8 +47,10 @@ def requires_role(min_role_level=None, required_modules=None, page=None):
# Manual role level checking
if min_role_level:
user_level = ROLES.get(user_role, {}).get('level', 0)
debug_info['user_level'] = user_level
debug_info['access_granted'] = user_level >= min_role_level
if user_level < min_role_level:
flash('Access denied: Insufficient privileges.')
flash(f'Access denied: Insufficient privileges. (Your level: {user_level}, Required: {min_role_level})')
return redirect(url_for('main.dashboard'))
# Module requirement checking

View File

@@ -383,12 +383,21 @@ def create_users_table_mariadb():
username VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
role VARCHAR(50) NOT NULL,
modules JSON DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
"""
cursor.execute(users_table_query)
print_success("Table 'users' created successfully")
# Ensure modules column exists (for backward compatibility with existing tables)
try:
cursor.execute("SELECT modules FROM users LIMIT 1")
except mariadb.ProgrammingError:
# Column doesn't exist, add it
cursor.execute("ALTER TABLE users ADD COLUMN modules JSON DEFAULT NULL")
print_success("Added 'modules' column to existing 'users' table")
# Create roles table in MariaDB
roles_table_query = """
CREATE TABLE IF NOT EXISTS roles (

View File

@@ -4,6 +4,24 @@ Clear hierarchy: Superadmin → Admin → Manager → Worker
Module-based permissions: Quality, Labels, Warehouse
"""
# Role mapping for normalization
ROLE_MAPPING = {
'superadmin': 'superadmin',
'super_admin': 'superadmin',
'super-admin': 'superadmin',
'administrator': 'admin',
'admin': 'admin',
'manager': 'manager',
'worker': 'worker',
}
def normalize_role(role):
"""Normalize role name to match ROLES dictionary"""
if not role:
return None
role_lower = str(role).lower().strip()
return ROLE_MAPPING.get(role_lower, role_lower)
# APPLICATION MODULES
MODULES = {
'quality': {
@@ -117,6 +135,9 @@ def check_access(user_role, user_modules, page):
Returns:
bool: True if access granted, False otherwise
"""
# Normalize role name
user_role = normalize_role(user_role)
if user_role not in ROLES:
return False

View File

@@ -103,7 +103,15 @@ def login():
if user:
session['user'] = user['username']
session['role'] = user['role']
session.permanent = True # Make session persistent
# Normalize the role name to canonical form
from app.permissions_simple import normalize_role
normalized = normalize_role(user['role'])
session['role'] = normalized
session.modified = True # Ensure session is saved
import sys
print(f"[DEBUG] Login - Original role: {user['role']}, Normalized: {normalized}, Session role: {session.get('role')}, Permanent: {session.permanent}", file=sys.stderr)
# Load user's modules into session
user_modules = []
@@ -119,6 +127,7 @@ def login():
user_modules = ['quality', 'warehouse', 'labels', 'daily_mirror']
session['modules'] = user_modules
session.modified = True # Ensure all session changes are saved
# Check app license for non-superadmin users
if user['role'] != 'superadmin':
@@ -318,6 +327,7 @@ def user_management_simple():
cursor = conn.cursor()
cursor.execute("SHOW TABLES LIKE 'users'")
if cursor.fetchone():
# Select users with modules column
cursor.execute("SELECT id, username, role, modules FROM users")
for row in cursor.fetchall():
user_data = {
@@ -348,8 +358,9 @@ def user_management_simple():
return render_template('user_management_simple.html', users=users)
except Exception as e:
print(f"Error in user_management_simple: {e}")
flash('Error loading user management page.')
import traceback
error_details = traceback.format_exc()
flash(f'Error loading user management page: {str(e)} - {error_details}', 'danger')
return redirect(url_for('main.dashboard'))
@bp.route('/create_user_simple', methods=['POST'])

View File

@@ -949,7 +949,14 @@ function editUser(userId, username, role, modules) {
updateEditModuleSelection();
const modal = new bootstrap.Modal(document.getElementById('editUserModal'));
const modalElement = document.getElementById('editUserModal');
const modal = new bootstrap.Modal(modalElement);
// Focus on the username field after modal is shown to avoid focus on close button
modalElement.addEventListener('shown.bs.modal', function() {
document.getElementById('edit_username').focus();
}, { once: true });
modal.show();
}

View File

@@ -1,4 +1,3 @@
# Database Configuration - Generated on Fri Dec 26 17:40:32 EET 2025
server_domain=db
port=3306
database_name=trasabilitate

View File

@@ -1,12 +0,0 @@
from app import create_app, db
from app.models import User
app = create_app()
with app.app_context():
# Add only the superadmin user
user = User(username='superadmin', password='superadmin123', role='superadmin')
if not User.query.filter_by(username=user.username).first():
db.session.add(user)
db.session.commit()
print("Database seeded with only the superadmin user.")