Role-Based Views
Technical documentation for the role-based view system in sparQ.
Overview
As an old-school UNIX geek, I’ve built countless access control systems—ranging from simple to complex. Yet, I always come back to the elegance of the UNIX model. Inspired by its tried-and-true simplicity, I present here a clean, effective implementation of role-based views in sparQ using a familiar concept: groups. A group consists of multiple users, and a user can belong to multiple groups.
Built-in Groups:
- All: Every user is automatically a member.
- Admin: Users with administrative privileges.
- Custom groups (e.g., Sales, Engineering) can be created by admins.
The application implements group-based views through a layered approach combining decorators, route protection, seperate templates, and conditional template logic. This allows for a maintainable, flexible and secure way to manage access to different parts of the application. A normal user for instance can see another employees profile (contact info etc)but not sensitive personal information. An admin can see system wide settings but a normal user can only see their own settings.
Three Strategies for Role-Based Views
- Access Control Decorators (for route protection)
- Conditional Template Logic (for minor view differences)
- Separate Templates (for major view variations)
1. Access Control Decorator
Typically used in the controller to protect routes from unauthorized access. Routes are protected at the controller level using decorators:
Admin-only routes
@blueprint.route("/employees/new")
@login_required
@admin_required
def new_employee():
"""Only admins can create new employees"""
...
Shared routes with role-based views
@blueprint.route("/employees/<int:employee_id>")
@login_required
def employee_detail(employee_id):
"""Both admins and employees can view, but see different templates"""
is_admin = current_user.is_admin
is_self = current_user.id == employee.user_id
template = "employees/admin-view/details.html" if is_admin else "employees/employee-view/details.html"
return render_template(template, employee=employee)
2. Conditional Template Logic
For minor variations in content, a single template can be used with conditional logic.
<!-- Single template with role-based sections -->
<div class="employee-details">
<!-- Basic info visible to all -->
<h1>{{ employee.name }}</h1>
<p>{{ employee.department }}</p>
<!-- Admin-only actions -->
{% if current_user.is_admin %}
<div class="admin-actions">
<button class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">
{{ _("Delete Employee") }}
</button>
<button class="btn btn-primary" hx-get="{{ url_for('people_bp.edit_employee', id=employee.id) }}">
{{ _("Edit") }}
</button>
</div>
{% endif %}
{% if current_user.is_admin or current_user.id == employee.user_id %}
<!-- Sensitive info only for admins and self -->
<div class="sensitive-info">
<p>{{ _("Phone") }}: {{ employee.phone }}</p>
<p>{{ _("Email") }}: {{ employee.email }}</p>
</div>
{% endif %}
</div>
3. Separate Templates (Major View Differences)
If the UI varies significantly by role, separate templates improve clarity and maintainability.
modules/people/views/templates/employees/
├── admin-view/
│ └── details.html # Full administrative view
└── employee-view/
└── details.html # Limited employee view
Conclusion
This layered approach ensures: ✅ Security – Unauthorized users can’t access restricted views. ✅ Flexibility – Easy customization of access rules and UI. ✅ Maintainability – Reduces code duplication while keeping role-based logic clear.
By combining decorators, conditional logic, and separate templates where needed, sparQ offers a scalable way to manage role-based views while keeping the codebase clean and efficient.
Come and join our team by checking out the repo at github.com/sparqone/sparq.