-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Feature Description
Implement a comprehensive Role-Based Access Control (RBAC) system to manage user permissions across the SecPal platform, including temporal role assignments with automatic expiration and role/permission management API.
User Stories
- As an Admin, I want to assign roles to users so that they have appropriate access levels
- As an Admin, I want to create custom roles so that I can tailor permissions to specific needs
- As an Admin, I want to manage permissions so that I can control access granularly
- As a Manager, I want to control who can view/edit employees in my branch so that data stays secure
- As a Works Council member, I want restricted access to only relevant employee data so that privacy is protected
- As a Client, I want read-only access to my location's data so that I can monitor services
- As a Manager, I want to assign temporary roles with expiration dates for vacation coverage, projects, or events
- As an Admin, I want roles to automatically expire to ensure principle of least privilege
Acceptance Criteria
Core RBAC
- ADR-004 created documenting RBAC architecture decision (spatie/laravel-permission vs custom)
- Database migrations for
roles,permissions,role_usertables (Spatie provides these) - Predefined system roles created:
- Admin (full access)
- Manager (branch-scoped)
- Guard (own data + assignments)
- Client (read-only, location-scoped)
- Works Council (approval workflows, limited employee access)
- NEW: Role & Permission Management API
- CRUD endpoints for roles (system roles protected)
- CRUD endpoints for permissions
- Hybrid approach: system roles + custom roles
- Permission naming convention:
resource.action
- Policy classes implemented for major resources:
EmployeePolicy- who can view/edit employeesShiftPlanPolicy- who can create/approve/publish shift plansWorksCouncilPolicy- who can access BR-specific features- NEW:
RoleManagementPolicy- who can manage roles - NEW:
PermissionManagementPolicy- who can manage permissions
- Permission middleware applied to all API routes
- Scope-based access (branch, location, division)
Temporal Role Assignments
-
valid_fromandvalid_untilcolumns added touser_rolestable -
auto_revokeflag to control automatic expiration behavior - Eloquent scope
active()to filter roles by validity period - Scheduled command
roles:expireruns every minute to revoke expired roles - API endpoints for assigning roles with temporal constraints:
POST /api/v1/users/{id}/roles- with optionalvalid_from,valid_untilGET /api/v1/users/{id}/roles- includes expiration infoDELETE /api/v1/users/{id}/roles/{role}- revoke role assignmentPATCH /api/v1/users/{id}/roles/{role}/extend- extend expiration
- Notifications for role expiration:
- 7 days before: notify manager
- 24 hours before: notify user + manager
- On expiration: notify both parties
- Audit trail for role assignments (
role_assignments_logtable)
Role & Permission Management (Phase 4 - NEW)
- Role CRUD API (7 endpoints):
- List all roles (system + custom)
- Create custom role
- Get role details
- Update role (system: permissions only, custom: all)
- Delete custom role
- Assign/revoke permissions to/from role
- Permission CRUD API (5 endpoints):
- List all permissions (grouped by resource)
- Create permission
- Get permission details
- Update permission (description only)
- Delete permission (if not assigned)
- System vs Custom Roles:
- System roles: Protected names, cannot be deleted
- Custom roles: Fully manageable by Admin
is_system_roleflag in database
- Authorization:
- Only Admin can manage roles/permissions
- Policies enforce all operations
Testing & Quality
- Unit tests for all policies (>80% coverage) - Phase 1-3
- Integration tests for permission enforcement - Phase 1-3
- Unit tests for temporal role logic (expiration, auto-revoke) - Phase 2
- Feature tests for temporal API endpoints - Phase 3
- Test timezone edge cases (UTC storage, user timezone display) - Phase 2
- NEW: Feature tests for Role CRUD (12 tests)
- NEW: Feature tests for Permission CRUD (8 tests)
- NEW: Authorization tests for role management (6 tests)
- NEW: Integration tests for custom role lifecycle
- Documentation updated (API docs, README)
Technical Notes
Database Schema
// Base tables (Spatie Laravel-Permission)
roles:
- id (BIGINT), name, guard_name
- **NEW:** is_system_role (boolean, default: false)
- created_at, updated_at
permissions:
- id (BIGINT), name, guard_name
- created_at, updated_at
role_has_permissions:
- permission_id, role_id
model_has_roles (pivot table):
- model_type, model_id (UUID), role_id, tenant_id (BIGINT)
- **Phase 1 additions:**
- valid_from (timestamp, nullable) - role becomes active
- valid_until (timestamp, nullable) - role expires
- auto_revoke (boolean, default: true) - auto-delete on expiry
- assigned_by (BIGINT, foreign key to users) - who assigned the role
- reason (text, nullable) - justification for assignment
- INDEX: (valid_from, valid_until)
// Audit trail (Phase 2)
role_assignments_log:
- id (UUID)
- user_id (BIGINT)
- role_id (BIGINT)
- action (enum: 'assigned', 'revoked', 'expired', 'extended')
- valid_from, valid_until (timestamp)
- assigned_by (BIGINT)
- reason (text)
- created_atTemporal Logic Implementation
// Eloquent Scope for active roles (Phase 1)
public function scopeActive($query)
{
return $query->where(function ($q) {
$q->whereNull('valid_from')
->orWhere('valid_from', '<=', now());
})->where(function ($q) {
$q->whereNull('valid_until')
->orWhere('valid_until', '>', now());
});
}
// Scheduled command (runs every minute) - Phase 2
// Console/Commands/ExpireRoles.php
public function handle()
{
$expired = TemporalRoleUser::expired()->cursor();
foreach ($expired->chunk(100) as $chunk) {
DB::transaction(function () use ($chunk) {
foreach ($chunk as $assignment) {
// 1. Delete assignment
$deleted = DB::table('model_has_roles')
->where('model_id', $assignment->model_id)
->where('role_id', $assignment->role_id)
->where('auto_revoke', true)
->where('valid_until', '<', now())
->delete();
// 2. Log to audit trail (only if deleted)
if ($deleted > 0) {
RoleAssignmentLog::create([
'user_id' => $assignment->model_id,
'role_id' => $assignment->role_id,
'action' => 'expired',
'valid_from' => $assignment->valid_from,
'valid_until' => $assignment->valid_until,
]);
}
}
});
}
}
// Trait: HasTemporalRoles (Phase 1)
public function hasRole($role): bool
{
return $this->roles()
->active() // Only consider valid roles
->where('name', $role)
->exists();
}Notification Schedule
// Schedule in routes/console.php
Schedule::command('roles:notify-expiring --days=7')->daily();
Schedule::command('roles:notify-expiring --days=1')->daily();
Schedule::command('roles:expire')->everyMinute();Example Permissions (Phase 4)
Format: resource.action
employees.read
employees.create
employees.update
employees.delete
employees.read_salary (restricted)
employees.read_all_branches (admin only)
shifts.read
shifts.create
shifts.update
shifts.delete
shifts.publish
shifts.approve_as_br (works council only)
work_instructions.read
work_instructions.create
work_instructions.update
work_instructions.delete
work_instructions.publish
work_instructions.acknowledge
work_instructions.view_acknowledgments
roles.read
roles.create
roles.update
roles.delete
roles.assign_temporary (manager/admin only)
roles.extend_expiration (manager/admin only)
permissions.read
permissions.create
permissions.update
permissions.delete
works_council.access_employee_files
works_council.approve_shift_plans
System Roles (Phase 4)
Roles with is_system_role=true are protected:
| Role | Description | Can Delete? | Can Rename? | Can Change Permissions? |
|---|---|---|---|---|
| Admin | Full system access | ❌ No | ❌ No | ✅ Yes |
| Manager | Branch management | ❌ No | ❌ No | ✅ Yes |
| Guard | Own data + shifts | ❌ No | ❌ No | ✅ Yes |
| Client | Read-only access | ❌ No | ❌ No | ✅ Yes |
| Works Council | BR workflows | ❌ No | ❌ No | ✅ Yes |
Custom roles (created via API):
| Role | Description | Can Delete? | Can Rename? | Can Change Permissions? |
|---|---|---|---|---|
| Regional Manager | Example custom | ✅ Yes* | ✅ Yes | ✅ Yes |
*Only if not assigned to any users
Spatie vs Custom Evaluation Criteria
| Criterion | Spatie | Custom |
|---|---|---|
| Multi-tenancy support | ✅ Good | ✅ Full control |
| Scope-based permissions | ✅ Native | |
| Works Council workflows | ✅ Tailored | |
| Temporal assignments | ✅ Native support | |
| Auto-expiry | ❌ Not built-in | ✅ Full control |
| Role Management API | 🟢 Use with extensions | ✅ Native support |
| Complexity | 🟢 Low | 🟠 Medium |
| Maintenance | 🟢 Community | 🔴 Self |
Decision (ADR-004): Hybrid approach using Spatie with custom temporal extensions and role management API
Use Cases for Temporal Roles
1. Vacation Coverage
Manager A on vacation (2025-12-01 to 2025-12-14)
→ Manager B gets Manager A's permissions temporarily
→ Auto-revoke on 2025-12-14 23:59:59 UTC
2. Project-Based Access
External auditor needs read access for 1 week
→ Assign "Auditor" role with valid_until = 2025-11-13 23:59:59
→ Notifications 24h before expiry
→ Auto-revoke on expiration
3. Event-Based Elevation
Guard becomes "Team Lead" for large event (2025-11-15 18:00 to 2025-11-16 06:00)
→ Elevated permissions during event only
→ Automatic revert to Guard role after event
4. Compliance (Principle of Least Privilege)
Developer needs production access for hotfix
→ Assign "Admin" role with valid_until = +2 hours
→ Auto-revoke ensures access is removed after fix
→ Audit trail logs access duration
5. Custom Role for Regional Manager (NEW)
Organization needs "Regional Manager" role
→ Admin creates custom role via API
→ Assigns permissions: employees.*, shifts.*, work_instructions.read
→ Assigns role to regional managers
→ Can modify role permissions as needs evolve
Important Considerations
Timezone Handling
- Storage: All timestamps in UTC (database + application)
- Display: Convert to user's timezone in API responses
- Edge Cases: Document behavior when role expires during user's active session
Security
- Only Manager and Admin roles can assign temporal roles
- Only Admin role can manage roles/permissions (Phase 4)
- Cannot extend expiration beyond original assigner's permissions
- Audit log is immutable (no deletion allowed)
- API validates
valid_from < valid_until - System roles are protected from deletion
- Custom roles can only be deleted if not assigned to users
Performance
- Index on
(valid_from, valid_until)for fast active role queries - Index on
is_system_rolefor role management queries - Scheduled command processes in batches (100 roles per transaction) if >1000 expired roles
- Cache active roles per user (invalidate on assignment/revocation)
Phase Progress
-
✅ Phase 1: Foundation & Temporal Extensions (Closed - PRs feat: RBAC Phase 1 - Temporal Role Foundation #109, test: implement tenant-aware temporal role tests (Issue #110) #112, fix: resolve Copilot review comments from PR #112 #113)
- Custom pivot model with temporal columns
- Active/expired scopes
- 12 tests
-
✅ Phase 2: Temporal Logic & Auto-Expiration (Closed - PRs feat(rbac): audit trail infrastructure for role assignments (Phase 2 Part 1) #117, feat(rbac): Phase 2 - Temporal Logic & Auto-Expiration (#106) #118, perf(rbac): optimize ExpireRoles command for production #120)
roles:expirecommand with batch processing- Audit trail (
role_assignments_log) - 10 tests
-
✅ Phase 3: API Endpoints & Authorization (Closed - PR feat(rbac): API endpoints for role management (Phase 3) #121)
- Role assignment API (4 endpoints)
- Request validation
- Authorization with permissions
- 18 tests
-
⏳ Phase 4: Role/Permission Management, Documentation & Final Testing (Open - Issue RBAC Phase 4: Documentation & Final Testing #108)
- Role CRUD API (7 endpoints)
- Permission CRUD API (5 endpoints)
- System vs custom roles
- Seeder for predefined roles
- Integration tests
- Documentation
Dependencies
- Blocks:
- refactor: improve DRY and add pre-PR checklist #68 (Employee Management) - needs RBAC to control access
- docs: add Epic & Sub-Issue core workflow documentation #69 (Works Council Integration) - needs custom BR role
- docs: add Issue 50 retrospective and case study #70 (Shift Planning) - needs approval permissions
- Phase 1: Backend API Foundation for Work Instructions #102 (Work Instructions Backend) - needs RBAC for authorization
Reference
docs/feature-requirements.md- Section "RBAC & Permission System"docs/legal-compliance.md- GDPR access control requirements- ADR-004: RBAC Architecture Decision (merged in .github repo)
Milestone
🎯 v0.2.0 - Core authentication/authorization
Labels
priority: blocker🔴type: feature✨component: apieffort: XL(3-4 weeks with temporal features + role management)
Sub-issues
Metadata
Metadata
Assignees
Type
Projects
Status