Auditoría del sistema de permisos a partir del 404 en /app/doc/paginas. 1. La comparación de permisos usaba solo el último segmento de la URL (lo que va después del último '/'). Rompía en las dos direcciones: con permiso sobre /app/doc/categorias se entraba a cualquier otra ruta terminada en "categorias", y a la vez el permiso parecía no aplicarse donde sí correspondía porque el segmento coincidía por casualidad. Ahora se compara la ruta completa, cortando en el separador para que /app/doc/paginas no habilite /app/doc/paginas-privadas. 2. El submódulo de Documentación nunca se sembró, aunque las rutas y las vistas existen desde mayo. Había que crear el ítem del menú a mano, y una URL mal tipeada ahí se ve exactamente igual que un permiso mal asignado: un 404. Ahora se siembra con la URL correcta. 3. El submódulo "Statuspage" apuntaba a /app/statuspage, que no existe en ninguna parte — la página real es la pública /status. Se corrige el seed y también el registro ya creado en la base. 4. Chequeo en el arranque que recorre los submódulos de la base y avisa cuáles apuntan a una URL sin ruta. Cubre los creados a mano, que es justo donde el compilador y los tests no llegan. 5. Test que cruza las URLs sembradas contra las rutas registradas: fue el que encontró lo de statuspage. Queda pendiente y es más grande: de 454 rutas protegidas solo 54 pasan por MenuMiddleware. El permiso gatea la página HTML pero no los endpoints de datos — cualquier usuario autenticado puede llamar a GET /app/doc/loadpaginas o DELETE /app/doc/paginas/:id sin tener el submódulo asignado. Se reporta antes de tocarlo porque cerrarlo de golpe puede dejar gente afuera. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
50 lines
1.8 KiB
Go
50 lines
1.8 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")
|
|
}
|
|
}
|