Primera tanda del hueco reportado en el commit anterior: el permiso tapaba
la página HTML pero no los datos. Cualquier usuario con sesión podía pedir
GET /app/loadusers o DELETE /app/doc/paginas/:id escribiendo la URL, sin
tener el submódulo asignado.
No alcanzaba con poner MenuMiddleware en esas rutas: compara la ruta pedida
contra los submódulos del rol, y el endpoint de datos vive en otra ruta que
la pantalla (/app/loadusers alimenta /app/users). Compararla consigo misma
nunca coincidiría y dejaría afuera hasta a quien sí tiene el permiso.
RequiereModulo("/app/users") declara en la ruta a qué pantalla pertenece el
endpoint, y valida ese submódulo. El administrador sigue entrando a todo,
igual que en MenuMiddleware.
Esta tanda cubre lo que más duele: identidad y permisos (usuarios, roles,
módulos, submódulos), donde una fuga es escalada de privilegios, y
Documentación, que es el módulo que disparó la auditoría.
Quedan las otras tandas (contabilidad, facturas, clientes, servidores...).
Se hace por partes a propósito: cerrar 400 rutas de una puede dejar gente
afuera de pantallas que hoy usa.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
73 lines
2.9 KiB
Go
73 lines
2.9 KiB
Go
package middlewares
|
|
|
|
import "testing"
|
|
|
|
// El permiso comparaba solo el último segmento de la URL, así que dos rutas
|
|
// distintas terminadas igual eran indistinguibles. Estos casos fijan que la
|
|
// comparación es sobre la ruta completa y corta en el separador.
|
|
func TestPuedeVerRuta(t *testing.T) {
|
|
permisos := []string{"/app/doc/paginas", "/app/clientes", "/status"}
|
|
|
|
casos := []struct {
|
|
ruta string
|
|
esperado bool
|
|
porque string
|
|
}{
|
|
{"/app/doc/paginas", true, "coincidencia exacta"},
|
|
{"/app/doc/paginas/7", true, "el detalle cuelga de la pantalla permitida"},
|
|
{"/app/doc/paginas/", true, "la barra final no cambia nada"},
|
|
{"/app/clientes", true, "otra pantalla permitida"},
|
|
{"/status", true, "una ruta pública también puede ser un ítem del menú"},
|
|
|
|
{"/app/doc/categorias", false, "otra pantalla del mismo módulo"},
|
|
{"/app/umind", false, "sin permiso"},
|
|
{"/app/doc", false, "el padre no se hereda del hijo"},
|
|
|
|
// El bug original: comparaba solo lo que va después del último '/'.
|
|
{"/app/otro/paginas", false, "termina igual pero es otra ruta"},
|
|
// El corte va en el separador, no en el prefijo de texto.
|
|
{"/app/doc/paginas-privadas", false, "solo comparte el comienzo del nombre"},
|
|
{"/app/clientes-vip", false, "prefijo parecido, ruta distinta"},
|
|
}
|
|
|
|
for _, cas := range casos {
|
|
if got := PuedeVerRuta(cas.ruta, permisos); got != cas.esperado {
|
|
t.Errorf("PuedeVerRuta(%q) = %v, esperaba %v — %s", cas.ruta, got, cas.esperado, cas.porque)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sin permisos no se entra a ningún lado (los administradores se resuelven
|
|
// antes, con user.IsAdmin).
|
|
func TestPuedeVerRutaSinPermisos(t *testing.T) {
|
|
if PuedeVerRuta("/app/clientes", nil) {
|
|
t.Error("un rol sin submódulos no debería acceder a nada")
|
|
}
|
|
if PuedeVerRuta("/app/clientes", []string{"", " "}) {
|
|
t.Error("una URL vacía no debería habilitar nada")
|
|
}
|
|
}
|
|
|
|
// RequiereModulo compara contra el submódulo DECLARADO en la ruta, no contra
|
|
// la ruta pedida. Es lo que permite proteger /app/loadusers con el permiso de
|
|
// la pantalla /app/users: compararlo consigo mismo nunca coincidiría y
|
|
// bloquearía incluso a quien sí tiene el permiso.
|
|
func TestRequiereModuloUsaElSubmoduloDeclarado(t *testing.T) {
|
|
permisos := []string{"/app/users"}
|
|
|
|
// El endpoint de datos vive en otra ruta que la pantalla.
|
|
if !PuedeVerRuta("/app/users", permisos) {
|
|
t.Error("con el permiso de la pantalla se debe poder pedir sus datos")
|
|
}
|
|
// Y comparar la ruta del endpoint contra el permiso NO coincide: por eso
|
|
// hace falta declarar el submódulo en vez de mirar c.Path().
|
|
if PuedeVerRuta("/app/loadusers", permisos) {
|
|
t.Error("la ruta del endpoint no coincide con la del submódulo — " +
|
|
"si coincidiera, no haría falta RequiereModulo")
|
|
}
|
|
// Sin el permiso, no se accede.
|
|
if PuedeVerRuta("/app/users", []string{"/app/clientes"}) {
|
|
t.Error("con otro permiso no se debe acceder a los datos de usuarios")
|
|
}
|
|
}
|