Visión general — qué hace
Los roles y los equipos son etiquetas informativas para clasificar a las personas. Lo único que abre el acceso a pantallas y acciones es el nivel (user_level). Lo que un usuario puede ver, y lo que puede guardar o eliminar, lo decide al 100 % el nivel; los roles («DBA», «DevOps», «Seguridad») y los equipos («Equipo de Infraestructura», «Equipo de Seguridad») son etiquetas de visualización y organización que no otorgan ningún permiso.
Así se mantiene simple el modelo de permisos. Para dar a alguien poder de administrador se sube su nivel, no se cambia su rol. Etiquetar a un usuario como «Seguridad» no abre las pantallas de seguridad; su nivel debe ser Personal (5) o superior.
Este artículo cubre tres cosas:
- Roles (
infr_role) — seis etiquetas de puesto (DBA, DevOps, Seguridad, Administrador, Personal, Desarrollador). - Equipos (
infr_team) — unidades organizativas, usadas para la pertenencia de usuarios y la responsabilidad sobre activos. - El selector de responsable — un desplegable combinado que permite asignar un usuario o un equipo, con
App\Support\Ownerscomo su única fuente de verdad. El campo «responsable» de Instancias, Hardware y Almacenamiento lo utiliza.
Control de acceso = solo el nivel (
user_level). Los roles y los equipos no abren ningún permiso de forma programática. Consulte Comprender los niveles (RBAC) para el sistema de niveles.
Cuándo usarlo
- Al incorporar a una persona nueva y etiquetar su puesto (rol) y su pertenencia (equipo).
- En una reorganización: crear un equipo o cambiar su responsable.
- Al asignar un responsable (una persona o un equipo) a activos de servidor, hardware o almacenamiento, para que quede explícito «quién responde por esto».
- Auditoría o traspaso: consultar «quién es el responsable de este activo».
Estructura de la pantalla
/admin/users (Administración ▸ Usuarios y roles) tiene dos pestañas.
| Pestaña | Contenido |
|---|---|
| Usuarios | Lista de usuarios (ID de acceso · nombre · correo · rol · equipo · nivel · estado · última conexión) + CRUD en modal |
| Equipos | Lista de equipos (nombre · color · responsable · descripción · número de miembros) + CRUD en modal |
El modal de edición de usuario tiene selectores de nivel / rol / equipo, seguidos de esta indicación:
El nivel rige el acceso: Demo (lectura), Personal (operar), Administrador (todo). El rol es solo una etiqueta.
Cómo usarlo
Roles — etiquetas de puesto
El maestro de roles tiene seis entradas, insertadas o actualizadas de forma idempotente por key mediante InfrRoleSeeder.
| key | Etiqueta | Token de color | seq |
|---|---|---|---|
dba |
DBA | info | 10 |
devops |
DevOps | accent | 20 |
security |
Seguridad | crit | 30 |
admin |
Administrador | warn | 40 |
staff |
Personal | muted | 50 |
developer |
Desarrollador | good | 60 |
- Un usuario tiene como máximo un rol (
users.role_id, admite nulo). Puede quedar sin asignar. - Los roles aparecen únicamente como etiqueta y color en el desplegable de usuario de la barra superior y en las insignias de las listas.
- La etiqueta mostrada la resuelve
App\Support\RolePolicy::roleLabel($roleId)con una caché de 60 segundos (llame aRolePolicy::flush()tras los cambios).
El rol
admin(etiqueta «Administrador») no tiene nada que ver con el nivel de administrador (10). Un rol es solo una etiqueta, así que un usuario cuyo rol sea «Administrador» pero cuyo nivel sea Demo (1) no puede guardar nada. A la inversa, un administrador de nivel 10 dispone de toda la administración aunque su rol esté vacío.
Equipos — unidades organizativas
Los equipos se gestionan libremente con CRUD en la pestaña Equipos. Los cuatro equipos de la semilla:
| Nombre | Color | Descripción |
|---|---|---|
| Equipo de Infraestructura | accent | Operación de servidores / red / almacenamiento |
| Equipo de Seguridad | crit | Detección · respuesta · bloqueo |
| Equipo de Desarrollo | good | Desarrollo de servicios · despliegue |
| Equipo DBA | info | Operación de bases de datos · optimización |
Campos de edición de equipo:
| Campo | Significado |
|---|---|
Nombre (name) |
Obligatorio. Nombre del equipo |
Color (color) |
Tono de la insignia (accent/crit/good/info/muted …) |
Responsable (lead_user_id) |
Un usuario (opcional) |
Descripción (description) |
Texto libre |
Activo (is_active) |
Los equipos inactivos quedan ocultos en el selector de responsable |
- Un usuario pertenece como máximo a un equipo (
users.team_id, admite nulo). - Al eliminar un equipo: el
team_idde los miembros de ese equipo pasa anull(los usuarios permanecen, ahora sin asignar). Los activos que referenciaban al equipo medianteowner_type='team'son un asunto aparte, de modo que los activos en sí no se eliminan.
El selector de responsable — usuarios y equipos combinados
Los activos (instancias de servidor, hardware, almacenamiento) tienen un campo «responsable» que puede ser una persona o un equipo. Las reglas de este selector combinado residen en App\Support\Owners (la SOT).
Forma de almacenamiento = dos columnas
La tabla del activo guarda al responsable en dos columnas.
| Columna | Significado |
|---|---|
owner_id |
PK entera de un usuario o de un equipo |
owner_type |
'user' o 'team' |
Forma de intercambio en el frontend = una única cadena ref
El navegador (formularios, filtros) intercambia owner_type y owner_id como una sola cadena.
user:5 → user id 5
team:3 → team id 3
Las opciones de arranque (Owners::options()) devuelven los dos grupos para el renderizado en optgroup (caché de 20 segundos). Se excluyen los usuarios dados de baja (is_close=1) y los equipos inactivos (is_active=0).
{
"users": [ { "ref": "user:5", "id": 5, "name": "Jane Doe" } ],
"teams": [ { "ref": "team:3", "id": 3, "name": "Infra Team" } ]
}
División y reconstrucción en el servidor
| Ayudante | Comportamiento |
|---|---|
Owners::split($ref) |
"user:5" → ['user', 5]. Formato incorrecto → ['user', 0] (= sin asignar) |
Owners::ref($ownerId, $ownerType) |
(5, 'user') → "user:5". ownerId vacío → null |
Owners::resolveName($ownerId, $ownerType) |
Nombre mostrado en tarjetas y listas (null si no hay, caché de 20 segundos) |
Owners::flush() |
Invalida la caché tras cambios de usuario o de equipo |
Por ejemplo, el controlador de guardado de instancias hace esto:
// ref del responsable ("user:5"/"team:3") → owner_id + owner_type
[$ownerType, $ownerId] = Owners::split($request->input('owner_ref'));
$payload['owner_id'] = $ownerId ?: null;
$payload['owner_type'] = $ownerId ? $ownerType : 'user'; // 'user' por defecto si no se indica
La misma ref se utiliza al filtrar por responsable: la consulta de la lista divide el parámetro owner (user:5/team:3) con split() y acota a WHERE owner_id = ? AND owner_type = ?.
El selector de responsable se reutiliza de forma idéntica en las pantallas de activos Instancias, Hardware y Almacenamiento y archivos. Asignar un equipo como responsable pone a todo el equipo en la línea de responsabilidad (resistente a la marcha de cualquier persona).
Avatar · iniciales · color
El avatar que se muestra junto a una etiqueta lo genera RolePolicy a partir del nombre.
- Iniciales (
initials): CJK/coreano → primer carácter; nombres latinos → primera letra de la primera y de la última palabra (2 caracteres). - Tono (
hue):crc32(name) % 360para un fondo HSL estable. - Tono de nivel (
tierTone): administrador (≥10) = crit · personal/operación (≥2) = accent · demo = muted.
Fuentes de datos
| Tabla | Propósito |
|---|---|
infr_role |
Maestro de roles (6 etiquetas informativas) — key·name·color·seq·is_active |
infr_team |
Maestro de equipos — name·color·lead_user_id·description·is_active·seq |
users |
Usuarios — user_level (decide el acceso), role_id (admite nulo), team_id (admite nulo) |
| tablas de activos | Responsable guardado en dos columnas, owner_id + owner_type (user/team) (Instancias/Hardware/Almacenamiento, etc.) |
Ayudantes SOT:
App\Support\Owners— selector combinado de responsable (options/split/ref/resolveName/flush).App\Support\RolePolicy— etiquetas y tonos de nivel, etiquetas de rol (caché de 60 segundos), iniciales y color del avatar.
Acceso
- La entrada a toda la pantalla es solo para administradores.
/admin/*está protegido por el middleware de grupoinfr.level:10, yUserController/TeamControllervuelven a comprobar por sí mismos eluser_level >= 10de la sesión (RolePolicy::ADMIN). Demo (1) y Personal (5) obtienen un 403. - Umbrales de nivel (referencia): escritura ≥ 2 (
RolePolicy::WRITE, bloquea a demo) · administración ≥ 10 (ADMIN) · desarrollo/observabilidad ≥ 9 (DEV, incluye al administrador 10). - Al guardar un usuario,
user_levelqueda restringido a uno de1,5,10. - Todo cambio de usuario o de equipo se audita en
infr_audit_log(team.create/team.update/team.delete, guardado y borrado de usuarios, etc.).
Salvaguardas (protección del último administrador)
| Situación | Resultado |
|---|---|
| Degradar / desactivar / eliminar al último administrador activo | Rechazado: «El último administrador activo no puede ser degradado, desactivado ni eliminado.» |
| Bajar o desactivar su propio nivel de administrador | Rechazado: «No puedes degradar ni desactivar tu propio acceso de administrador.» |
| Eliminarse a sí mismo | Rechazado: «No puedes eliminarte a ti mismo.» |
| ID de acceso ya en uso | Rechazado: «Ese ID de acceso ya está en uso.» |
Resolución de problemas y preguntas frecuentes
P. He dado a alguien el rol «Seguridad» pero las pantallas de seguridad no se abren. Los roles son etiquetas; no abren ningún permiso. Las pantallas de seguridad (en su mayoría de lectura) necesitan un nivel: súbalo a Personal (5) o superior. Consulte Niveles RBAC.
P. Falta algún usuario o equipo en el desplegable de responsable.
Los usuarios dados de baja (is_close=1) y los equipos inactivos (is_active=0) quedan excluidos del selector. Reactive el usuario o el equipo o, si la lista está desactualizada, espere a que expire la caché de 20 segundos (Owners::flush() la invalida de inmediato).
P. Si elimino un equipo, ¿desaparecen sus personas?
No. Los usuarios permanecen; solo su team_id pasa a null (sin asignar). La referencia owner_type='team' de un activo es independiente, de modo que el activo tampoco se elimina (simplemente queda vacío el responsable mostrado).
P. He guardado un responsable pero no aparece ningún nombre.
resolveName() tiene una caché de 20 segundos. Si acaba de renombrar algo, se actualizará en breve. Confirme también que el formato de la ref es user:5/team:3: un formato roto lo trata split() como sin asignar (user:0).
P. ¿Puede un usuario tener dos roles?
No. role_id es un valor único (que admite nulo). Si además necesita una agrupación organizativa, utilice un equipo (también un valor único).
Relacionado
- Comprender los niveles (RBAC) — el único eje que realmente decide el acceso
- Consola de usuarios y equipos — detalles del CRUD de
/admin/users - Instancias — la pantalla de activos insignia que usa el selector de responsable
- Hardware — responsables de activos físicos
- Almacenamiento y archivos — responsables de activos de almacenamiento
- Registro de auditoría — historial de cambios de usuarios, equipos y responsables
