BREAKING CHANGE: Permission system now actively enforced across all routes ## Summary Complete implementation of advanced role-based access control (RBAC) system with full route protection, UI conditionals, and enhanced management interface. ## Route Protection - Updated all admin routes to use @admin_or_permission_required decorator - Replaced inline admin checks with granular permission checks in: * Admin routes: user management, settings, backups, telemetry, OIDC * Project routes: create, edit, delete, archive, bulk operations * Client routes: create, edit, delete, archive, bulk operations - Maintained backward compatibility with existing @admin_required decorator ## UI Permission Integration - Added template helpers (has_permission, has_any_permission) to all templates - Navigation conditionally shows admin/OIDC links based on permissions - Action buttons (Edit, Delete, Archive) conditional on user permissions - Project and client pages respect permission requirements - Create buttons visible only with appropriate permissions ## Enhanced Roles & Permissions UI - Added statistics dashboard showing: * Total roles, system roles, custom roles, assigned users - Implemented expandable permission details in roles list * Click to view all permissions grouped by category * Visual checkmarks for assigned permissions - Enhanced user list with role visibility: * Shows all assigned roles as color-coded badges * Blue badges for system roles, gray for custom roles * Yellow badges for legacy roles with migration prompt * Merged legacy role column into unified "Roles & Permissions" - User count per role now clickable and accurate ## Security Improvements - Added CSRF tokens to all new permission system forms: * Role creation/edit form * Role deletion form * User role assignment form - All POST requests now protected against CSRF attacks ## Technical Details - Fixed SQLAlchemy relationship query issues (AppenderQuery) - Proper use of .count() for relationship aggregation - Jinja2 namespace for accumulating counts in templates - Responsive grid layouts for statistics and permission cards ## Documentation - Created comprehensive implementation guides - Added permission enforcement documentation - Documented UI enhancements and features - Included CSRF protection review ## Impact - Permissions are now actively enforced, not just defined - Admins can easily see who has what access - Clear visual indicators of permission assignments - Secure forms with CSRF protection - Production-ready permission system
12 KiB
Advanced Permission Handling System
Overview
TimeTracker now includes a comprehensive, role-based permission system that allows administrators to control access to various features and functionality at a granular level. This system replaces the simple "admin" vs "user" model with a flexible role-based access control (RBAC) system.
Key Concepts
Permissions
Permissions are individual capabilities or actions that a user can perform in the system. Examples include:
view_all_time_entries- View time entries from all userscreate_projects- Create new projectsedit_invoices- Edit invoice detailsmanage_settings- Access and modify system settings
Each permission has:
- Name: A unique identifier (e.g.,
edit_projects) - Description: Human-readable explanation of what the permission allows
- Category: Logical grouping (e.g.,
projects,invoices,system)
Roles
Roles are collections of permissions that can be assigned to users. Instead of granting individual permissions to each user, you assign roles that bundle related permissions together.
Examples of roles:
- Super Admin: Full system access with all permissions
- Admin: Most administrative capabilities except role management
- Manager: Can oversee projects, tasks, and team members
- User: Standard access for time tracking and personal data
- Viewer: Read-only access
Each role has:
- Name: Unique identifier
- Description: Explanation of the role's purpose
- System Role Flag: Indicates whether the role is built-in (cannot be deleted)
- Permissions: Collection of permissions assigned to the role
Users and Roles
Users can be assigned one or more roles. A user's effective permissions are the union of all permissions from their assigned roles.
Default Roles and Permissions
System Roles
The following system roles are created by default:
Super Admin
- All permissions in the system
- Can manage roles and permissions themselves
- Intended for system administrators
Admin
- All permissions except role/permission management
- Can manage users, projects, invoices, settings
- Cannot modify the permission system itself
Manager
- Oversight capabilities for teams and projects
- Can view all time entries and reports
- Can create and edit projects, tasks, and clients
- Can create and send invoices
- Cannot delete users or modify system settings
User
- Standard time tracking capabilities
- Can create and edit own time entries
- Can create and manage own tasks
- View-only access to projects and clients
- Can view own reports and invoices
Viewer
- Read-only access
- Can view own time entries, tasks, and reports
- Cannot create or modify anything
Permission Categories
Permissions are organized into the following categories:
Time Entries
view_own_time_entriesview_all_time_entriescreate_time_entriesedit_own_time_entriesedit_all_time_entriesdelete_own_time_entriesdelete_all_time_entries
Projects
view_projectscreate_projectsedit_projectsdelete_projectsarchive_projectsmanage_project_costs
Tasks
view_own_tasksview_all_taskscreate_tasksedit_own_tasksedit_all_tasksdelete_own_tasksdelete_all_tasksassign_tasks
Clients
view_clientscreate_clientsedit_clientsdelete_clientsmanage_client_notes
Invoices
view_own_invoicesview_all_invoicescreate_invoicesedit_invoicesdelete_invoicessend_invoicesmanage_payments
Reports
view_own_reportsview_all_reportsexport_reportscreate_saved_reports
User Management
view_userscreate_usersedit_usersdelete_usersmanage_user_roles
System
manage_settingsview_system_infomanage_backupsmanage_telemetryview_audit_logs
Administration (Super Admin Only)
manage_rolesmanage_permissionsview_permissions
Using the Permission System
For Administrators
Viewing Roles
- Navigate to Admin Dashboard → Roles & Permissions
- View all available roles with their permission counts
- Click on a role to see detailed information and assigned users
Creating Custom Roles
- Go to Admin Dashboard → Roles & Permissions
- Click Create Role
- Enter:
- Role name (e.g., "Project Manager")
- Description (optional)
- Select permissions by category
- Click Create Role
Note: Custom roles can be modified or deleted. System roles cannot be deleted but serve as templates for custom roles.
Editing Roles
- Navigate to the role list
- Click Edit on a custom role (system roles cannot be edited)
- Modify name, description, or permissions
- Click Update Role
Assigning Roles to Users
- Go to Admin Dashboard → Manage Users
- Click Edit on a user
- Click Manage Roles & Permissions
- Select the roles to assign
- Click Update Roles
Users can have multiple roles. Their effective permissions will be the combination of all assigned roles.
Viewing User Permissions
- Edit a user in the admin panel
- Click Manage Roles & Permissions
- Scroll to "Current Effective Permissions" to see all permissions the user has
For Developers
Checking Permissions in Code
Use the permission checking methods on the User model:
from flask_login import current_user
# Check single permission
if current_user.has_permission('edit_projects'):
# Allow editing
# Check if user has ANY of the permissions
if current_user.has_any_permission('edit_projects', 'delete_projects'):
# Allow action
# Check if user has ALL of the permissions
if current_user.has_all_permissions('create_invoices', 'send_invoices'):
# Allow action
Using Permission Decorators
Protect routes with permission decorators:
from app.utils.permissions import permission_required
@app.route('/projects/<id>/edit')
@login_required
@permission_required('edit_projects')
def edit_project(id):
# Only users with edit_projects permission can access
pass
# Require multiple permissions (user needs ANY of them)
@app.route('/reports/export')
@login_required
@permission_required('view_all_reports', 'export_reports')
def export_report():
pass
# Require ALL permissions
@app.route('/admin/critical')
@login_required
@permission_required('manage_settings', 'manage_backups', require_all=True)
def critical_admin_action():
pass
Admin or Permission Required
For gradual migration, use the admin_or_permission_required decorator:
from app.utils.permissions import admin_or_permission_required
@app.route('/projects/delete')
@login_required
@admin_or_permission_required('delete_projects')
def delete_project():
# Admins OR users with delete_projects permission can access
pass
Checking Permissions in Templates
Use the template helpers to conditionally show UI elements:
{% if has_permission('edit_projects') %}
<a href="{{ url_for('projects.edit', id=project.id) }}">Edit Project</a>
{% endif %}
{% if has_any_permission('create_invoices', 'edit_invoices') %}
<button>Manage Invoices</button>
{% endif %}
{% if has_all_permissions('view_all_reports', 'export_reports') %}
<a href="{{ url_for('reports.export') }}">Export All Reports</a>
{% endif %}
Migration from Legacy System
Backward Compatibility
The new permission system is fully backward compatible with the existing "role" field:
- Users with
role='admin'are automatically recognized as administrators - Legacy admin users have all permissions (even without assigned roles)
- The
is_adminproperty checks both the legacy role field and new role assignments
Migrating Existing Users
To migrate users to the new system:
-
Run the migration command:
flask seed_permissions_cmd -
This will:
- Create all default permissions
- Create all default roles
- Migrate existing users:
- Users with
role='admin'get the "admin" role - Users with
role='user'get the "user" role
- Users with
-
Optionally, review and adjust role assignments in the admin panel
Updating Permissions After Updates
If new permissions are added in a system update:
flask update_permissions
This command updates permissions and roles without affecting user assignments.
Database Schema
Tables
permissions
id- Primary keyname- Unique permission identifierdescription- Human-readable descriptioncategory- Permission categorycreated_at- Timestamp
roles
id- Primary keyname- Unique role identifierdescription- Role descriptionis_system_role- Boolean flagcreated_at- Creation timestampupdated_at- Last update timestamp
role_permissions (Association Table)
role_id- Foreign key to rolespermission_id- Foreign key to permissionscreated_at- Assignment timestamp
user_roles (Association Table)
user_id- Foreign key to usersrole_id- Foreign key to rolesassigned_at- Assignment timestamp
API Endpoints
Get User Permissions
GET /api/users/<user_id>/permissions
Returns:
{
"user_id": 1,
"username": "john",
"roles": [
{"id": 1, "name": "manager"}
],
"permissions": [
{"id": 1, "name": "view_all_time_entries", "description": "..."},
{"id": 2, "name": "create_projects", "description": "..."}
]
}
Get Role Permissions
GET /api/roles/<role_id>/permissions
Returns:
{
"role_id": 1,
"name": "manager",
"description": "Team Manager with oversight capabilities",
"is_system_role": true,
"permissions": [
{"id": 1, "name": "view_all_time_entries", "category": "time_entries", "description": "..."}
]
}
Best Practices
Creating Custom Roles
- Start with a system role: Use system roles as templates
- Be specific: Create roles for specific job functions (e.g., "Invoice Manager", "Project Lead")
- Least privilege: Grant only the permissions needed for the role's purpose
- Document: Add clear descriptions to custom roles
Permission Naming
- Use snake_case:
create_projects, notCreateProjects - Action first:
edit_invoices, notinvoices_edit - Be specific:
view_all_time_entriesvsview_own_time_entries
Testing Permissions
Always test permission changes:
- Create a test user
- Assign the role
- Log in as that user
- Verify they can/cannot access expected features
Troubleshooting
User Cannot Access Feature
- Check user's assigned roles
- Verify the roles have the required permission
- Check if the feature requires multiple permissions
- Ensure user account is active
Cannot Edit/Delete Role
- System roles cannot be edited or deleted
- Roles assigned to users cannot be deleted (reassign users first)
Legacy Admin Lost Permissions
If a legacy admin user (with role='admin') loses permissions:
- Verify their
rolefield is still 'admin' - If using new role system, assign them the "super_admin" or "admin" role
- The system checks both legacy role and new roles
Permission Changes Not Taking Effect
- Log out and log back in
- Permissions are loaded on login
- Check browser cache/session
Security Considerations
- Super Admin Role: Assign sparingly - it has full system access
- Regular Audits: Review user role assignments periodically
- Separation of Duties: Don't assign conflicting roles (e.g., invoice creation + approval)
- Testing: Always test in a non-production environment first
Future Enhancements
Planned features:
- Permission inheritance: Hierarchical permissions
- Time-based roles: Temporary role assignments
- Audit logging: Track permission changes
- Role templates: Exportable role configurations
- API keys with permissions: Scoped API access
Support
For issues or questions about the permission system:
- Check this documentation
- Review the test files:
tests/test_permissions.pyandtests/test_permissions_routes.py - Check the implementation:
app/models/permission.pyandapp/utils/permissions.py