From 03455374e50b5716464012bcf09b2fe7b7aeba70 Mon Sep 17 00:00:00 2001 From: Jens Luedicke Date: Thu, 3 Jul 2025 01:33:32 +0200 Subject: [PATCH] Add SYSTEM_ADMINISTRATOR role. --- app.py | 33 +++++++++++++++++++++++++++++---- migrate_db.py | 8 ++++++++ models.py | 3 ++- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/app.py b/app.py index 949b9aa..db91422 100644 --- a/app.py +++ b/app.py @@ -681,17 +681,37 @@ def login_required(f): def admin_required(f): @wraps(f) def decorated_function(*args, **kwargs): - if g.user is None or g.user.role != Role.ADMIN: + if g.user is None or (g.user.role != Role.ADMIN and g.user.role != Role.SYSTEM_ADMIN): flash('You need administrator privileges to access this page.', 'error') return redirect(url_for('home')) return f(*args, **kwargs) return decorated_function +# System Admin-only decorator +def system_admin_required(f): + @wraps(f) + def decorated_function(*args, **kwargs): + if g.user is None or g.user.role != Role.SYSTEM_ADMIN: + flash('You need system administrator privileges to access this page.', 'error') + return redirect(url_for('home')) + return f(*args, **kwargs) + return decorated_function + def get_system_setting(key, default='false'): """Helper function to get system setting value""" setting = SystemSettings.query.filter_by(key=key).first() return setting.value if setting else default +def is_system_admin(user=None): + """Helper function to check if user is system admin""" + if user is None: + user = g.user + return user and user.role == Role.SYSTEM_ADMIN + +def can_access_system_settings(user=None): + """Helper function to check if user can access system-wide settings""" + return is_system_admin(user) + # Add this decorator function after your existing decorators def role_required(min_role): """ @@ -704,8 +724,8 @@ def role_required(min_role): if g.user is None: return redirect(url_for('login', next=request.url)) - # Admin always has access - if g.user.role == Role.ADMIN: + # Admin and System Admin always have access + if g.user.role == Role.ADMIN or g.user.role == Role.SYSTEM_ADMIN: return f(*args, **kwargs) # Check role hierarchy @@ -713,7 +733,8 @@ def role_required(min_role): Role.TEAM_MEMBER: 1, Role.TEAM_LEADER: 2, Role.SUPERVISOR: 3, - Role.ADMIN: 4 + Role.ADMIN: 4, + Role.SYSTEM_ADMIN: 5 } if role_hierarchy.get(g.user.role, 0) < role_hierarchy.get(min_role, 0): @@ -733,6 +754,10 @@ def company_required(f): if g.user is None: return redirect(url_for('login', next=request.url)) + # System admins can access without company association + if g.user.role == Role.SYSTEM_ADMIN: + return f(*args, **kwargs) + if g.user.company_id is None: flash('You must be associated with a company to access this page.', 'error') return redirect(url_for('setup_company')) diff --git a/migrate_db.py b/migrate_db.py index beb7adb..986accb 100644 --- a/migrate_db.py +++ b/migrate_db.py @@ -293,6 +293,14 @@ def migrate_database(): updated = True if updated: updated_count += 1 + + # Check if any system admin users exist + system_admin_count = User.query.filter_by(role=Role.SYSTEM_ADMIN).count() + if system_admin_count == 0: + print("No system administrators found. Consider promoting a user to SYSTEM_ADMIN role manually.") + print("Use: UPDATE user SET role = 'System Administrator' WHERE username = 'your_username';") + else: + print(f"Found {system_admin_count} system administrator(s)") db.session.commit() print(f"Associated {len(orphan_entries)} existing time entries with admin user") diff --git a/models.py b/models.py index 0af1fa1..4a0b048 100644 --- a/models.py +++ b/models.py @@ -11,7 +11,8 @@ class Role(enum.Enum): TEAM_MEMBER = "Team Member" TEAM_LEADER = "Team Leader" SUPERVISOR = "Supervisor" - ADMIN = "Administrator" # Keep existing admin role + ADMIN = "Administrator" # Company-level admin + SYSTEM_ADMIN = "System Administrator" # System-wide admin # Define Account Type for freelancer support class AccountType(enum.Enum):