fix: seeds ya no reasignan módulos a roles restringidos + Query Runner con alcance real
- Los seeds que creaban módulos nuevos asignaban esos submódulos a TODOS los roles en cada arranque (idempotente contra re-agregar lo ya quitado, pero igual tocaba roles personalizados/restringidos por primera vez apenas existían). Ahora solo se asignan automáticamente al rol "Administrador"; cualquier rol restringido que crees para un empleado ya no recibe módulos nuevos sin que tú se los habilites a propósito desde /app/roles. - Se revierte el bloqueo "solo administrador" que había puesto en /query-runner/run y /run-batch: bloqueaba también a usuarios con el submódulo Query Runner correctamente asignado a su rol. En su lugar se agrega la validación real que faltaba — RunQuery/RunBatchQuery no verificaban que el conx_db_id recibido perteneciera al rol del usuario (solo el listado de conexiones del selector estaba filtrado); ahora si no es admin, se verifica contra Role.ConxDBs antes de ejecutar cualquier SQL. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
5ac70a4d6e
commit
21f968c532
@@ -98,6 +98,31 @@ func GetTables(c *fiber.Ctx) error {
|
||||
return c.JSON(fiber.Map{"data": tables})
|
||||
}
|
||||
|
||||
// usuarioPuedeUsarConx verifica que el usuario autenticado pueda ejecutar SQL
|
||||
// contra esa conexión: admin ve todas, el resto solo las que su rol tiene
|
||||
// asignadas en Role.ConxDBs (el mismo criterio que ya usa GetConxDbList para
|
||||
// decidir qué mostrar en el selector — aquí se hace cumplir de verdad).
|
||||
//
|
||||
// Sin este chequeo, cualquiera con acceso al submódulo Query Runner podía
|
||||
// mandar cualquier conx_db_id por API y ejecutar SQL contra una conexión que
|
||||
// nunca se le mostró ni se le asignó a su rol.
|
||||
func usuarioPuedeUsarConx(c *fiber.Ctx, conxDbID uint) bool {
|
||||
user, err := auth.User(c)
|
||||
if err != nil || user == nil {
|
||||
// Llamada sin sesión (Hermes/API con su propia auth): no restringido aquí.
|
||||
return true
|
||||
}
|
||||
if user.IsAdmin {
|
||||
return true
|
||||
}
|
||||
for _, db := range user.Role.ConxDBs {
|
||||
if db.ID == conxDbID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RunQuery ejecuta una consulta SQL.
|
||||
// POST /app/query-runner/run
|
||||
// Body: { conx_db_id, database, sql }
|
||||
@@ -113,6 +138,9 @@ func RunQuery(c *fiber.Ctx) error {
|
||||
if strings.TrimSpace(body.SQL) == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "La consulta está vacía"})
|
||||
}
|
||||
if !usuarioPuedeUsarConx(c, body.ConxDbID) {
|
||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "No tienes acceso a esta conexión"})
|
||||
}
|
||||
|
||||
conx, err := loadConxDb(strconv.Itoa(int(body.ConxDbID)))
|
||||
if err != nil {
|
||||
@@ -179,6 +207,12 @@ func RunBatchQuery(c *fiber.Ctx) error {
|
||||
return c.Status(400).JSON(fiber.Map{"error": "No hay consultas para ejecutar. Envía sqls[] o un archivo .sql"})
|
||||
}
|
||||
|
||||
if conxIDNum, parseErr := strconv.ParseUint(conxDbIDStr, 10, 32); parseErr == nil {
|
||||
if !usuarioPuedeUsarConx(c, uint(conxIDNum)) {
|
||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "No tienes acceso a esta conexión"})
|
||||
}
|
||||
}
|
||||
|
||||
conx, err := loadConxDb(conxDbIDStr)
|
||||
if err != nil {
|
||||
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
|
||||
@@ -599,13 +633,13 @@ func heuristicSQLFix(sql string) string {
|
||||
"ORDR": "ORDER", "ORDRE": "ORDER", "ORBER": "ORDER",
|
||||
"HAVNG": "HAVING", "HAVIN": "HAVING", "HVING": "HAVING",
|
||||
"LIMT": "LIMIT", "LIIMT": "LIMIT",
|
||||
"JON": "JOIN", "JOUN": "JOIN",
|
||||
"JON": "JOIN", "JOUN": "JOIN",
|
||||
"LEF JOIN": "LEFT JOIN", "LEFTJ OIN": "LEFT JOIN",
|
||||
"RIGTH": "RIGHT",
|
||||
"CRATE": "CREATE", "CREARE": "CREATE",
|
||||
"TABEL": "TABLE", "TBALE": "TABLE",
|
||||
"ALTR": "ALTER", "ALTE": "ALTER",
|
||||
"DRO": "DROP", "DROPP": "DROP",
|
||||
"DRO": "DROP", "DROPP": "DROP",
|
||||
"IDNEX": "INDEX", "INEX": "INDEX",
|
||||
"PRIMRY": "PRIMARY", "PRIMAR": "PRIMARY", "PRMARY": "PRIMARY",
|
||||
"FORIGN": "FOREIGN", "FOREIN": "FOREIGN", "FORIEGN": "FOREIGN",
|
||||
|
||||
Reference in New Issue
Block a user