🔐 Sistema RBAC (Role-Based Access Control)
Visión General
PuntoHack implementa un sistema RBAC completo con 5 roles y permisos bien definidos. Cada acción en el sistema verifica los permisos del usuario antes de ejecutarse.
Jerarquía de Roles
Matriz de Permisos Completa
| Acción | Descripción | Admin | Organizador | Juez | Sponsor | Participante | Notas |
|---|---|---|---|---|---|---|---|
| Cambiar roles | Asignar roles a usuarios | ✅ | ❌ | ❌ | ❌ | ❌ | - |
| Gestionar usuarios | CRUD de usuarios (excepto cambiar roles) | ✅ | ✅ | ❌ | ❌ | ❌ | ORGANIZER no puede cambiar roles |
| Crear hackathon | Crear y configurar hackathons | ✅ | ✅ | ❌ | ❌ | ❌ | - |
| Asignar jueces | Asignar jueces a hackathons | ✅ | ✅ | ❌ | ❌ | ❌ | - |
| Evaluar proyectos | Asignar scores a submissions | ✅ | ❌ | ✅ | ❌ | ❌ | JUDGE solo hackathons asignados |
| Ver scores de otros | Ver scores de otros jueces | ✅ | ✅ | ❌ | ❌ | ❌ | JUDGE solo en FINISHED |
| Crear challenges | Crear challenges para sponsorships | ✅ | ❌ | ❌ | ✅ | ❌ | - |
| Shortlist proyectos | Marcar proyectos favoritos | ✅ | ❌ | ❌ | ✅ | ❌ | - |
| Registrarse en hackathon | Participar en hackathons | ❌ | ❌ | ❌ | ❌ | ✅ | Solo estado REGISTRATION |
| Formar equipos | Crear y unirse a equipos | ❌ | ❌ | ❌ | ❌ | ✅ | Hasta submissionDeadline |
Notas importantes:
- ORGANIZER puede gestionar usuarios excepto cambiar roles a ADMIN
- JUDGE solo evalúa hackathons asignados
- JUDGE NO puede ver scores de otros durante JUDGING, solo en FINISHED
- JUDGE NO puede participar en el mismo hackathon que juzga
Implementación Técnica
Core RBAC (src/core/rbac.ts)
import { Role } from '@prisma/client';
export type User = {
id: string;
profile: {
id: string;
role: Role;
};
};
/**
* Verifica si el usuario tiene uno de los roles especificados
*/
export function hasRole(user: User, roles: Role[]): boolean {
return roles.includes(user.profile.role);
}
/**
* Requiere que el usuario tenga uno de los roles especificados
* Lanza error si no tiene el rol requerido
*/
export function requireRole(user: User, roles: Role[]): void {
if (!hasRole(user, roles)) {
throw new Error('Unauthorized: Insufficient permissions');
}
}
Uso en Server Actions
'use server';
import { getCurrentUser } from '@/core/auth';
import { requireRole } from '@/core/rbac';
import { db } from '@/core/db';
export async function createHackathon(formData: FormData) {
// 1. Obtener usuario actual
const user = await getCurrentUser();
if (!user) {
throw new Error('Unauthorized');
}
// 2. Verificar rol
requireRole(user, ['ORGANIZER', 'ADMIN']);
// 3. Lógica de negocio
const hackathon = await db.hackathon.create({
data: {
// ...
},
});
return { success: true, hackathon };
}
Permisos por Rol
ADMIN - "Dios Mode"
Acceso completo a todo el sistema:
- ✅ Cambiar roles: Puede asignar cualquier rol, incluido ADMIN
- ✅ Gestionar usuarios: CRUD completo, eliminar usuarios
- ✅ Gestionar hackathons: Ver, crear, editar, eliminar todos los hackathons
- ✅ Asignar jueces: Asignar jueces a cualquier hackathon
- ✅ Ver scores: Ver todos los scores de todos los jueces
- ✅ Override de validaciones: Puede forzar cambios que normalmente no están permitidos
- ✅ Gestión de organizaciones: CRUD completo de organizations, sponsorships, challenges
Restricciones en MVP: Ninguna (futuro: audit logs)
ORGANIZER
Gestiona hackathons y participantes:
- ✅ Crear hackathons: Crear y configurar hackathons
- ✅ Gestionar hackathons propios: Editar, publicar, extender fechas
- ✅ Asignar jueces: Asignar jueces a sus hackathons
- ✅ Ver estadísticas: Ver participaciones, equipos, submissions
- ✅ Gestionar participantes: Eliminar participaciones, expulsar de equipos
- ❌ Cambiar roles: No puede cambiar roles (solo ADMIN)
- ❌ Evaluar: No puede evaluar proyectos
- ❌ Gestionar otros organizadores: Solo ve sus propios hackathons
Nota: Puede gestionar usuarios (eliminar participaciones, expulsar de equipos) pero NO cambiar roles.
JUDGE
Evalúa proyectos asignados:
- ✅ Ver submissions: Ver submissions del hackathon asignado (solo en estado JUDGING)
- ✅ Evaluar proyectos: Asignar scores por criterio
- ✅ Dar feedback: Agregar comentarios por criterio
- ❌ Ver scores de otros: No puede ver scores de otros jueces durante JUDGING (solo en FINISHED)
- ❌ Participar en mismo hackathon: No puede registrarse ni formar equipo en el hackathon que juzga
- ❌ Editar scores: No puede editar scores enviados (solo ADMIN puede override)
Restricciones importantes:
- Solo evalúa hackathons asignados (
HackathonJudgeexiste) - Solo ve submissions cuando hackathon está en estado
JUDGING - No puede ver scores de otros jueces hasta que hackathon esté
FINISHED
SPONSOR
Gestiona challenges y shortlist:
- ✅ Crear challenges: Crear challenges para sus sponsorships
- ✅ Ver submissions: Ver submissions que eligieron sus challenges
- ✅ Shortlist proyectos: Marcar proyectos favoritos
- ✅ Ver perfiles: Ver perfiles completos de usuarios en shortlist
- ✅ Contactar equipos: Enviar emails vía Clerk sin exponer correos
- ❌ Evaluar: No puede asignar scores (solo jueces)
- ❌ Ver scores individuales: Solo ve resultado final en leaderboard
Nota: Puede ver leaderboard final y ganador de su challenge.
PARTICIPANT
Participa en hackathons:
- ✅ Registrarse: Registrarse en hackathons (estado REGISTRATION)
- ✅ Formar equipos: Crear equipos con códigos de invitación
- ✅ Unirse a equipos: Unirse con código de invitación
- ✅ Enviar submissions: Enviar proyectos antes de deadline
- ✅ Editar submissions: Editar hasta
submissionDeadline - ✅ Ver leaderboard: Ver leaderboard cuando hackathon esté FINISHED
- ❌ Evaluar: No puede evaluar proyectos
- ❌ Gestionar hackathons: No puede crear ni gestionar hackathons
Restricciones temporales:
- Crear/unirse/salir de equipo solo hasta
submissionDeadline - Editar submission solo hasta
submissionDeadline
Flujo de Verificación de Permisos
Casos Especiales
1. JUDGE no puede participar en mismo hackathon
Validación:
// Al asignar juez
export async function assignJudge(hackathonId: string, judgeId: string) {
// Verificar que no participa
const participates = await judgeParticipatesInHackathon(hackathonId, judgeId);
if (participates) {
throw new Error('Judge cannot participate in the same hackathon they judge');
}
// Crear asignación
await db.hackathonJudge.create({
data: { hackathonId, profileId: judgeId },
});
}
2. ORGANIZER puede gestionar usuarios pero no cambiar roles
Validación:
export async function updateUserRole(profileId: string, newRole: Role) {
const user = await getCurrentUser();
requireRole(user, ['ADMIN', 'ORGANIZER']);
// Solo ADMIN puede asignar ADMIN
if (newRole === 'ADMIN' && user.profile.role !== 'ADMIN') {
throw new Error('Only admins can assign ADMIN role');
}
// Actualizar rol
await db.profile.update({
where: { id: profileId },
data: { role: newRole },
});
}
3. JUDGE no ve scores de otros durante JUDGING
Query filtrada:
export async function getScoresForSubmission(
submissionId: string,
judgeId?: string
) {
const where: {
submissionId: string;
judgeId?: string;
} = {
submissionId,
};
// Si es juez, solo ver sus propios scores
if (judgeId) {
where.judgeId = judgeId;
}
return db.score.findMany({ where });
}
Extensión del Sistema
Agregar Nuevo Rol
- Agregar al enum en Prisma:
enum Role {
PARTICIPANT
JUDGE
ORGANIZER
ADMIN
SPONSOR
NEW_ROLE // Nuevo rol
}
- Actualizar matriz de permisos en esta documentación
- Agregar validaciones en Server Actions correspondientes
- Actualizar UI para mostrar opciones según rol
Agregar Nuevo Permiso
- Definir el permiso en la matriz
- Implementar validación en Server Action:
export async function newAction() {
const user = await getCurrentUser();
requireRole(user, ['ROLE_WITH_PERMISSION']);
// ...
}
Próximos Pasos
- Module Structure - Cómo los módulos usan RBAC
- User Flows - Flujos por rol
- Development Guide - Estándares de código
Siguiente: Module Structure