Módulo: Roles y Permisos
Scope
| Tipo de organización | Acceso |
|---|---|
| Empresa | ✅ |
| Agencia | ✅ |
| Reclutador independiente | ❌ |
Permisos por rol
Empresa (COMPANY)
| Acción | Disponible | Permiso |
|---|---|---|
| Ver lista de roles | ✅ | roles:read |
| Ver detalle de rol | ✅ | roles:read |
| Ver permisos disponibles | ✅ | roles:read |
| Crear rol personalizado | ✅ | roles:create |
| Editar rol personalizado | ✅ | roles:update |
| Eliminar rol personalizado | ✅ | roles:delete |
| Ver miembros de un rol | ✅ | roles:read + memberships:read |
Agencia (AGENCY)
| Acción | Disponible | Permiso |
|---|---|---|
| Ver lista de roles | ✅ | roles:read |
| Ver detalle de rol | ✅ | roles:read |
| Ver permisos disponibles | ✅ | roles:read |
| Crear rol personalizado | ✅ | roles:create |
| Editar rol personalizado | ✅ | roles:update |
| Eliminar rol personalizado | ✅ | roles:delete |
| Ver miembros de un rol | ✅ | roles:read + memberships:read |
Reclutador independiente (RECRUITER)
| Acción | Disponible | Permiso |
|---|---|---|
| Ver roles | ❌ | — |
| Crear/editar/eliminar roles | ❌ | — |
¿Qué es?
El módulo de Roles permite a las organizaciones (empresas y agencias) crear roles personalizados con permisos granulares para sus miembros. Los roles son plantillas de acceso que se asignan a los usuarios a través de membresías.
Existen dos tipos de roles:
- Globales (sistema): Roles predefinidos por la plataforma según tipo de organización (ej.
company_admin,agency_admin,recruiter). No se pueden modificar ni eliminar. - Personalizados (tenant): Roles creados por la organización con permisos seleccionados del catálogo disponible para su tipo.
¿Qué puede hacer el usuario?
Admin de organización
- Ver lista paginada de roles con búsqueda
- Ver detalle de un rol con permisos agrupados por recurso y conteo de miembros
- Crear roles personalizados con nombre, key auto-generado, descripción y permisos seleccionados
- Editar nombre, descripción y permisos de roles personalizados
- Eliminar roles personalizados (solo si no tienen miembros activos asignados)
- Ver qué usuarios tienen un rol específico
Flujo del usuario
Listar y gestionar roles
1. Abre "Equipo" desde la navegación
2. Selecciona el tab "Roles"
3. Ve la lista paginada con scroll infinito
4. Usa el buscador para filtrar roles por nombre/key
5. Puede ver detalle, editar o eliminar roles desde el menú contextual
Crear un rol
1. En el tab "Roles", presiona "Crear rol"
2. Se abre el modal bottom sheet
3. Ingresa nombre (la key se auto-genera)
4. Opcionalmente agrega descripción
5. Selecciona permisos del catálogo agrupado por recurso
6. Confirma la creación
7. El rol aparece en la lista
Editar un rol
1. Desde la lista o el detalle, presiona "Editar"
2. Se abre el modal con datos actuales pre-cargados
3. Modifica nombre, descripción o permisos
4. Confirma los cambios
5. El rol se actualiza
Eliminar un rol
1. Desde el menú contextual de la card, presiona "Eliminar"
2. Se muestra diálogo de confirmación
3. Confirma → el rol se elimina (soft-delete)
4. Si tiene miembros activos, el API rechaza con ROLE_IN_USE
Ver miembros de un rol
1. En el detalle del rol, presiona "Ver usuarios (N)"
2. Se abre la página con lista paginada de miembros asignados
3. Muestra avatar, nombre, email y estado de cada miembro
Reglas de negocio
- Los roles del sistema (globales) no se pueden crear, editar ni eliminar desde el tenant
- Un rol personalizado requiere al menos 1 permiso
- La key del rol se auto-genera del nombre, debe ser única dentro del tenant
- Las keys de roles globales están reservadas y no se pueden usar para roles personalizados
- Los permisos asignables están filtrados por tipo de organización y por los permisos que tiene el usuario activo
- Permisos internos (
system:access,admin:override) nunca son asignables a roles personalizados - No se puede eliminar un rol que tiene membresías activas asignadas
- La eliminación es soft-delete con auditoría
- Toda operación de CRUD invalida el cache de auth del tenant en Redis
Arquitectura
lib/features/team/
├── application/
│ ├── roles_cubit.dart # CRUD + paginación + permisos
│ └── roles_state.dart # Equatable, enum status, selectedRole
├── data/
│ ├── datasources/
│ │ └── team_remote_datasource.dart # 6 métodos CRUD
│ ├── models/
│ │ └── role_model.dart # RoleModel, RolePermissionModel, RolesResponseModel
│ └── repositories/
│ └── team_repository_impl.dart # Error handling tipado
├── domain/
│ ├── entities/
│ │ ├── role.dart # Role (Equatable), RolePermission (Equatable)
│ │ ├── available_permission.dart
│ │ └── membership.dart
│ └── repositories/
│ └── team_repository.dart # Contrato abstracto CRUD
└── presentation/
├── pages/
│ ├── role_detail_page.dart # Detalle con BlocProvider
│ └── role_members_page.dart # Miembros de un rol
├── tabs/
│ └── roles_tab.dart # Lista con BlocProvider + scroll infinito
└── widgets/
├── create_role_modal.dart # Bottom sheet con permisos
├── edit_role_modal.dart # Bottom sheet con permisos pre-seleccionados
└── role_card.dart # Card reutilizable
Endpoints API (sin prefijo /api)
| Método | Endpoint | Permiso | Descripción |
|---|---|---|---|
| GET | /roles | roles:read | Lista roles personalizados del tenant (paginado, search, sort) |
| GET | /roles/:roleId | roles:read | Detalle del rol con conteo de miembros |
| GET | /roles/available-permissions | roles:read | Permisos asignables para el tipo de organización |
| GET | /roles/assignable | roles:read | Roles asignables (globales + custom) para invitaciones |
| GET | /roles/:roleId/members | roles:read + memberships:read | Miembros asignados al rol (paginado) |
| POST | /roles | roles:create | Crear rol personalizado |
| PATCH | /roles/:roleId | roles:update | Actualizar rol personalizado |
| DELETE | /roles/:roleId | roles:delete | Soft-delete rol personalizado |
Respuesta paginada
{
"data": [...],
"meta": {
"page": 1,
"pageSize": 20,
"total": 5,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false
}
}
Estructura de un rol (response)
{
"id": "uuid",
"organizationId": "uuid | null",
"key": "senior_recruiter",
"name": "Senior Recruiter",
"description": "Can manage candidates and take vacancies.",
"scope": "TENANT",
"isSystem": false,
"permissions": [
{
"key": "vacancies:read",
"resource": "vacancies",
"action": "read",
"description": "Read visible vacancies under tenant scope."
}
],
"createdAt": "2026-05-11T16:00:00.000Z",
"updatedAt": "2026-05-11T16:00:00.000Z",
"membersCount": 3
}
Permisos asignables por tipo de organización
COMPANY
companies:read, companies:update, vacancies:read, vacancies:create, vacancies:publish, candidates:read, candidates:update-stage, disputes:read, disputes:create, wallet:read, billing:read, notifications:read, support:read, support:manage, ai:use-guide, y permisos de recruiting.
AGENCY
agencies:read, agencies:update, recruiters:read, recruiters:update, vacancies:read, vacancies:take, candidates:read, candidates:create, candidates:update-stage, disputes:read, disputes:create, wallet:read, notifications:read, support:read, support:manage, ai:use-guide, y permisos de recruiting.
RECRUITER
recruiters:read, recruiters:update, vacancies:read, vacancies:take, candidates:read, candidates:create, candidates:update-stage, disputes:read, disputes:create, wallet:read, notifications:read, support:read, ai:use-guide.
Registro DI
- Datasource:
LazySingleton - Repository:
LazySingleton - Cubit:
Factory(nueva instancia por pantalla)
getIt<RolesCubit>()
Localización
Usa claves l10n para labels del UI:
rolesDeleteConfirmTitle,rolesDeleteConfirmMessage,rolesDeleted,rolesDeleteErrorrolesLoadError,rolesEmpty,rolesManageDescription,rolesCount,rolesSearchHint,rolesCreateButtonretry,cancel,delete,close