Skip to content

Matriz de roles y permisos

Tabla exhaustiva de qué puede hacer cada rol. Implementado con Spatie Permission + Laravel Policies.

Los 4 roles

Enum App\Enums\UserRole:

  • candidate
  • recruiter
  • company_user
  • admin

Matriz de acceso por recurso

Leyenda: ✅ sí · 🔒 solo si es suyo · ⬜ no · 🏢 solo en su empresa

Auth

AcciónCandidatoRecruiterCompanyAdmin
Login
Logout
Register (auto)
Invitación🏢 (miembros)
Cambio de rol
Suspender usuarios

Perfil del candidato

AcciónCandidatoRecruiterCompanyAdmin
Leer perfil🔒✅ (directorio)✅ (si presentado)
Editar perfil🔒
Subir avatar🔒
Subir documentos🔒
Descargar CV PDF🔒✅ (solo del asignado)
Ver datos de contacto🔒⬜ (ocultos)
Cambiar estado⬜ (automático)

Membresía

AcciónCandidatoRecruiterCompanyAdmin
Iniciar checkout🔒
Ver mi membresía🔒
Ver histórico pagos🔒
Cancelar membresía
Refund✅ (manual en Stripe)

Psicométricas

AcciónCandidatoRecruiterCompanyAdmin
Ver pruebas disponibles🔒
Iniciar intento🔒
Enviar respuestas🔒
Ver resultados propios🔒
Ver scores del candidato🔒✅ (si presentado)
Ver respuestas individuales🔒
Crear/editar pruebas

Directorio

AcciónCandidatoRecruiterCompanyAdmin
Buscar candidatos
Ver detalle de candidato
Agregar a favoritos
Asignar a vacante

Empresas

AcciónCandidatoRecruiterCompanyAdmin
Ver datos de empresa🏢
Editar datos🏢 (owner)
Invitar miembros🚧 pospuesto🚧 pospuesto
Remover miembros🚧 pospuesto🚧 pospuesto
Crear company

Vacantes

AcciónCandidatoRecruiterCompanyAdmin
Ver listado global🏢
Crear vacante🏢
Editar vacante🏢
Publicar🏢
Cancelar🏢
Eliminar
Asignar recruiter

Pipeline / Asignaciones

AcciónCandidatoRecruiterCompanyAdmin
Ver asignaciones propias🔒🏢
Crear asignación
Cambiar stage
Ver notas recruiter
Ver notas empresa🏢
Escribir nota empresa🏢
Escribir nota recruiter

Entrevistas

AcciónCandidatoRecruiterCompanyAdmin
Listar propias🔒✅ (asignado)🏢
Agendar
Confirmar🔒🏢
Reprogramar🔒🏢
Cancelar🔒🏢
Marcar realizada + feedback
Marcar no_asisto

Notificaciones

AcciónCandidatoRecruiterCompanyAdmin
Ver propias🔒🔒🔒🔒
Marcar como leída🔒🔒🔒🔒
Eliminar🔒🔒🔒🔒

Admin / catálogos / reportes

AcciónCandidatoRecruiterCompanyAdmin
Gestionar catálogos
Ver reportes✅ (algunos)
Configuración global
Auditoría

Los 45 permisos (Spatie)

Agrupados en 11 categorías. Sembrados en RolesAndPermissionsSeeder.

CategoríaPermisos
Usersusers.view, users.create, users.update, users.delete, users.suspend, users.assign_role
Candidatescandidate.view_directory, candidate.view_detail, candidate.download_cv, candidate.favorite
Vacanciesvacancy.view, vacancy.create, vacancy.update, vacancy.publish, vacancy.cancel, vacancy.delete
Assignmentsassignment.create, assignment.update, assignment.delete, assignment.move_stage
Interviewsinterview.view, interview.schedule, interview.confirm, interview.reschedule, interview.cancel, interview.complete
Notesnote.create, note.update_own, note.delete_own, note.view_private
Psychometricstest.view, test.take, test.manage, result.view_own, result.view_any
Membershipsmembership.view_own, membership.cancel, payment.view, payment.refund
Companiescompany.view, company.update, company.manage_members, company.delete
Catalogscatalog.view, catalog.manage
Reportsreport.view, report.export

Asignación a roles

admin       → TODOS
recruiter   → candidate.*, vacancy.*, assignment.*, interview.*,
              note.*, test.view_any, result.view_any, report.view
company_user → vacancy.view (own company), assignment.view, note.create,
              interview.view/confirm/reschedule/cancel, candidate.view_detail (asignados)
candidate   → membership.view_own, test.take, result.view_own,
              interview.view/confirm/reschedule/cancel (propias), note.*

Implementación

En rutas

php
Route::middleware(['auth:sanctum', 'role:admin'])->group(function () {
    Route::apiResource('catalogos', CatalogController::class);
});

En controllers (Policies)

php
public function update(UpdateVacancyRequest $request, Vacancy $vacancy)
{
    $this->authorize('update', $vacancy);
    // ...
}

En VacancyPolicy

php
public function update(User $user, Vacancy $vacancy): bool
{
    if ($user->hasRole('admin')) return true;
    if ($user->hasRole('recruiter')) return true;
    if ($user->hasRole('company_user')) {
        return $user->companies()->where('companies.id', $vacancy->company_id)->exists();
    }
    return false;
}

Before bypass

Solo en Policies que lo documentan explícitamente:

php
public function before(User $user): ?bool
{
    return $user->hasRole('admin') ? true : null;
}

Usado con cuidado; se prefiere checks explícitos.

Frontend

  • useAuth() expone hasRole(role) y hasPermission(perm).
  • Componentes <ProtectedRoute> redirigen a /login si no hay rol.
  • Los botones de acción se renderizan condicionalmente:
tsx
{hasRole("recruiter") && <Button onClick={assignToVacancy}>Asignar</Button>}

Eso es UX — la autorización real vive en el backend.

Siguiente

Vocabulario común: Glosario →

Manual de usuario HUMAE · Uso interno