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