feat(auth): nuevo rol 'admin' (programador, modo Dios oculto)
JERARQUÍA DE ROLES
admin = programador. Modo Dios. Oculto de dropdowns y del Panel
de Control para non-admins. Acceso total.
socio = modo Dios visible. Acceso al Panel, ve y edita todo.
abogado = crea casos, ve los suyos + donde es involucrado.
usuario = solo ve/edita donde es involucrado.
* Migración 008: agrega 'admin' al ENUM users.role. Promueve a
ozerpa@aal-law.com como admin.
* Auth.php: isAdmin() nuevo. isSocio() y isAbogado() ahora incluyen
admin (admin hereda todos los poderes de socio). canCreate/canDelete
también heredan.
* Lookups.php:
- abogadoNames() ya filtraba a role IN (socio, abogado) → admins no
aparecen en el dropdown "Abogado Solicitante".
- involucrablesUsers() agrega filtro role != 'admin' → admins no
aparecen en el picker de Involucrados.
- allUsers($includeAdmins) parametrizada: socios reciben la lista
sin admins, admins reciben todo.
* Bootstrap.php: window.DONNA.isAdmin disponible al frontend.
* admin/api.php:
- list_users pasa Auth::isAdmin() para filtrar.
- create_user / update_user: rechazan role='admin' si quien llama no
es admin. Solo admins pueden editar a otros admins.
- delete_user: protege admins (extends al check existente de socio).
- Self-protection extendida: admin no puede demoverse por debajo de
socio (queda en god-mode tier).
* admin.html: opción 'admin' en role dropdown solo visible si el
usuario actual es admin. Role pill navy/blanca para admin.
This commit is contained in:
+5
-4
@@ -68,10 +68,11 @@ final class Auth {
|
||||
public static function role(): string { return (string)(self::userRow()['role'] ?? 'usuario'); }
|
||||
public static function userName(): string { return (string)(self::userRow()['name'] ?? ''); }
|
||||
|
||||
public static function isSocio(): bool { return self::role() === 'socio'; }
|
||||
public static function isAbogado(): bool { return in_array(self::role(), ['socio','abogado'], true); }
|
||||
public static function canCreate(): bool { return self::isAbogado(); } // socio + abogado pueden crear trámites
|
||||
public static function canDelete(): bool { return self::isAbogado(); } // socio + abogado pueden eliminar
|
||||
public static function isAdmin(): bool { return self::role() === 'admin'; }
|
||||
public static function isSocio(): bool { return in_array(self::role(), ['admin','socio'], true); }
|
||||
public static function isAbogado(): bool { return in_array(self::role(), ['admin','socio','abogado'], true); }
|
||||
public static function canCreate(): bool { return self::isAbogado(); } // admin + socio + abogado pueden crear trámites
|
||||
public static function canDelete(): bool { return self::isAbogado(); } // admin + socio + abogado pueden eliminar
|
||||
|
||||
public static function requireSocio(): void {
|
||||
self::requireAuth();
|
||||
|
||||
@@ -16,6 +16,7 @@ final class Bootstrap {
|
||||
'me' => $user['email'],
|
||||
'name' => $user['name'],
|
||||
'role' => Auth::role(),
|
||||
'isAdmin' => Auth::isAdmin(),
|
||||
'isSocio' => Auth::isSocio(),
|
||||
'isAbogado' => Auth::isAbogado(),
|
||||
'canCreate' => Auth::canCreate(),
|
||||
|
||||
+17
-10
@@ -8,7 +8,11 @@ require_once __DIR__ . '/Db.php';
|
||||
* dropdowns y resolver visibilidad.
|
||||
*/
|
||||
final class Lookups {
|
||||
/** Nombres completos para el dropdown "Abogado Solicitante" (socios + abogados activos). */
|
||||
/**
|
||||
* Nombres completos para el dropdown "Abogado Solicitante".
|
||||
* Solo socios y abogados; admins quedan ocultos (el programador no debe
|
||||
* aparecer en listas de abogados del firme).
|
||||
*/
|
||||
public static function abogadoNames(): array {
|
||||
$rows = Db::pdo()->query(
|
||||
"SELECT DISTINCT name FROM users
|
||||
@@ -20,12 +24,13 @@ final class Lookups {
|
||||
return array_map(fn($r) => $r['name'], $rows);
|
||||
}
|
||||
|
||||
/** Todos los usuarios activos para el picker de Involucrados. */
|
||||
/** Usuarios activos para el picker de Involucrados (sin admins ni placeholders). */
|
||||
public static function involucrablesUsers(): array {
|
||||
$rows = Db::pdo()->query(
|
||||
"SELECT id, email, name, role
|
||||
FROM users
|
||||
WHERE activo = 1
|
||||
AND role != 'admin'
|
||||
AND email NOT LIKE 'placeholder-%'
|
||||
ORDER BY name ASC, email ASC"
|
||||
)->fetchAll();
|
||||
@@ -33,14 +38,16 @@ final class Lookups {
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/** Todos los users (incluye inactivos y placeholders) para el Panel de Control. */
|
||||
public static function allUsers(): array {
|
||||
$rows = Db::pdo()->query(
|
||||
"SELECT id, email, name, role, activo, orden,
|
||||
first_seen_at, last_login_at
|
||||
FROM users
|
||||
ORDER BY role DESC, name ASC, email ASC"
|
||||
)->fetchAll();
|
||||
/**
|
||||
* Todos los users para el Panel de Control.
|
||||
* Si $includeAdmins es false, oculta el rol admin (uso para socios).
|
||||
*/
|
||||
public static function allUsers(bool $includeAdmins = true): array {
|
||||
$sql = "SELECT id, email, name, role, activo, orden, first_seen_at, last_login_at
|
||||
FROM users";
|
||||
if (!$includeAdmins) $sql .= " WHERE role != 'admin'";
|
||||
$sql .= " ORDER BY FIELD(role,'admin','socio','abogado','usuario'), name ASC, email ASC";
|
||||
$rows = Db::pdo()->query($sql)->fetchAll();
|
||||
foreach ($rows as &$r) {
|
||||
$r['id'] = (int)$r['id'];
|
||||
$r['activo'] = (int)$r['activo'];
|
||||
|
||||
Reference in New Issue
Block a user