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
+59
-49
@@ -238,14 +238,15 @@ func SeedStatuspage() {
|
|||||||
insertados = append(insertados, sub)
|
insertados = append(insertados, sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
var roles []models.Roles
|
// Solo se asigna al rol Administrador: los demás roles pueden estar
|
||||||
if err := db.Find(&roles).Error; err != nil {
|
// restringidos a propósito y no deben recibir módulos nuevos por su cuenta
|
||||||
log.Printf("[SEED] Error obteniendo roles: %v", err)
|
// — hay que habilitarlos manualmente desde /app/roles si corresponde.
|
||||||
|
var rolAdmin models.Roles
|
||||||
|
if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
|
||||||
|
log.Printf("[SEED] Rol 'Administrador' no encontrado: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, rol := range roles {
|
asignarSubmodulosSiFaltan(&rolAdmin, insertados)
|
||||||
asignarSubmodulosSiFaltan(&rol, insertados)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("[SEED] Seed de Statuspage completado.")
|
log.Println("[SEED] Seed de Statuspage completado.")
|
||||||
}
|
}
|
||||||
@@ -582,14 +583,15 @@ func SeedIntegraciones() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. Asignar a todos los roles existentes
|
// 3. Asignar a todos los roles existentes
|
||||||
var roles []models.Roles
|
// Solo se asigna al rol Administrador: los demás roles pueden estar
|
||||||
if err := db.Find(&roles).Error; err != nil {
|
// restringidos a propósito y no deben recibir módulos nuevos por su cuenta
|
||||||
log.Printf("[SEED] Error obteniendo roles: %v", err)
|
// — hay que habilitarlos manualmente desde /app/roles si corresponde.
|
||||||
|
var rolAdmin models.Roles
|
||||||
|
if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
|
||||||
|
log.Printf("[SEED] Rol 'Administrador' no encontrado: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, rol := range roles {
|
asignarSubmodulosSiFaltan(&rolAdmin, insertados)
|
||||||
asignarSubmodulosSiFaltan(&rol, insertados)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("[SEED] Seed de Integraciones completado.")
|
log.Println("[SEED] Seed de Integraciones completado.")
|
||||||
}
|
}
|
||||||
@@ -632,14 +634,15 @@ func SeedPasarelas() {
|
|||||||
insertados = append(insertados, sub)
|
insertados = append(insertados, sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
var roles []models.Roles
|
// Solo se asigna al rol Administrador: los demás roles pueden estar
|
||||||
if err := db.Find(&roles).Error; err != nil {
|
// restringidos a propósito y no deben recibir módulos nuevos por su cuenta
|
||||||
log.Printf("[SEED] Error obteniendo roles: %v", err)
|
// — hay que habilitarlos manualmente desde /app/roles si corresponde.
|
||||||
|
var rolAdmin models.Roles
|
||||||
|
if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
|
||||||
|
log.Printf("[SEED] Rol 'Administrador' no encontrado: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, rol := range roles {
|
asignarSubmodulosSiFaltan(&rolAdmin, insertados)
|
||||||
asignarSubmodulosSiFaltan(&rol, insertados)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("[SEED] Seed de Pasarelas completado.")
|
log.Println("[SEED] Seed de Pasarelas completado.")
|
||||||
}
|
}
|
||||||
@@ -705,14 +708,15 @@ func SeedServidores() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. Asignar a todos los roles existentes
|
// 3. Asignar a todos los roles existentes
|
||||||
var roles []models.Roles
|
// Solo se asigna al rol Administrador: los demás roles pueden estar
|
||||||
if err := db.Find(&roles).Error; err != nil {
|
// restringidos a propósito y no deben recibir módulos nuevos por su cuenta
|
||||||
log.Printf("[SEED] Error obteniendo roles: %v", err)
|
// — hay que habilitarlos manualmente desde /app/roles si corresponde.
|
||||||
|
var rolAdmin models.Roles
|
||||||
|
if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
|
||||||
|
log.Printf("[SEED] Rol 'Administrador' no encontrado: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, rol := range roles {
|
asignarSubmodulosSiFaltan(&rolAdmin, insertados)
|
||||||
asignarSubmodulosSiFaltan(&rol, insertados)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("[SEED] Seed de Servidores completado.")
|
log.Println("[SEED] Seed de Servidores completado.")
|
||||||
}
|
}
|
||||||
@@ -772,14 +776,15 @@ func SeedAdministracion() {
|
|||||||
insertados = append(insertados, sub)
|
insertados = append(insertados, sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
var roles []models.Roles
|
// Solo se asigna al rol Administrador: los demás roles pueden estar
|
||||||
if err := db.Find(&roles).Error; err != nil {
|
// restringidos a propósito y no deben recibir módulos nuevos por su cuenta
|
||||||
log.Printf("[SEED] Error obteniendo roles: %v", err)
|
// — hay que habilitarlos manualmente desde /app/roles si corresponde.
|
||||||
|
var rolAdmin models.Roles
|
||||||
|
if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
|
||||||
|
log.Printf("[SEED] Rol 'Administrador' no encontrado: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, rol := range roles {
|
asignarSubmodulosSiFaltan(&rolAdmin, insertados)
|
||||||
asignarSubmodulosSiFaltan(&rol, insertados)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("[SEED] Seed de Administración completado.")
|
log.Println("[SEED] Seed de Administración completado.")
|
||||||
}
|
}
|
||||||
@@ -823,14 +828,15 @@ func SeedSaas() {
|
|||||||
insertados = append(insertados, sub)
|
insertados = append(insertados, sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
var roles []models.Roles
|
// Solo se asigna al rol Administrador: los demás roles pueden estar
|
||||||
if err := db.Find(&roles).Error; err != nil {
|
// restringidos a propósito y no deben recibir módulos nuevos por su cuenta
|
||||||
log.Printf("[SEED] Error obteniendo roles: %v", err)
|
// — hay que habilitarlos manualmente desde /app/roles si corresponde.
|
||||||
|
var rolAdmin models.Roles
|
||||||
|
if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
|
||||||
|
log.Printf("[SEED] Rol 'Administrador' no encontrado: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, rol := range roles {
|
asignarSubmodulosSiFaltan(&rolAdmin, insertados)
|
||||||
asignarSubmodulosSiFaltan(&rol, insertados)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("[SEED] Seed de SaaS completado.")
|
log.Println("[SEED] Seed de SaaS completado.")
|
||||||
}
|
}
|
||||||
@@ -896,13 +902,15 @@ func SeedTelegram() {
|
|||||||
log.Printf("[SEED] Submódulo 'Telegram' ya existe (ID %d)", sub.ID)
|
log.Printf("[SEED] Submódulo 'Telegram' ya existe (ID %d)", sub.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
var roles []models.Roles
|
// Solo se asigna al rol Administrador: los demás roles pueden estar
|
||||||
if err := db.Find(&roles).Error; err != nil {
|
// restringidos a propósito y no deben recibir módulos nuevos por su cuenta
|
||||||
|
// — hay que habilitarlos manualmente desde /app/roles si corresponde.
|
||||||
|
var rolAdmin models.Roles
|
||||||
|
if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
|
||||||
|
log.Printf("[SEED] Rol 'Administrador' no encontrado: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, rol := range roles {
|
asignarSubmodulosSiFaltan(&rolAdmin, []models.Submodules{sub})
|
||||||
asignarSubmodulosSiFaltan(&rol, []models.Submodules{sub})
|
|
||||||
}
|
|
||||||
log.Println("[SEED] Seed de Telegram completado.")
|
log.Println("[SEED] Seed de Telegram completado.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -954,13 +962,15 @@ func SeedPortalClientes() {
|
|||||||
insertados = append(insertados, sub)
|
insertados = append(insertados, sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
var roles []models.Roles
|
// Solo se asigna al rol Administrador: los demás roles pueden estar
|
||||||
if err := db.Find(&roles).Error; err != nil {
|
// restringidos a propósito y no deben recibir módulos nuevos por su cuenta
|
||||||
|
// — hay que habilitarlos manualmente desde /app/roles si corresponde.
|
||||||
|
var rolAdmin models.Roles
|
||||||
|
if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
|
||||||
|
log.Printf("[SEED] Rol 'Administrador' no encontrado: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, rol := range roles {
|
asignarSubmodulosSiFaltan(&rolAdmin, insertados)
|
||||||
asignarSubmodulosSiFaltan(&rol, insertados)
|
|
||||||
}
|
|
||||||
log.Println("[SEED] Seed de Portal de Clientes completado.")
|
log.Println("[SEED] Seed de Portal de Clientes completado.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1134,7 +1144,7 @@ func SeedWebSms() {
|
|||||||
if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
|
if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
|
||||||
log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
|
log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
|
||||||
} else {
|
} else {
|
||||||
db.Model(&rolAdmin).Association("Submodules").Append(&[]models.Submodules{sub})
|
asignarSubmodulosSiFaltan(&rolAdmin, []models.Submodules{sub})
|
||||||
log.Printf("[SEED] Submódulo 'WebSMS' asignado al rol 'Administrador'")
|
log.Printf("[SEED] Submódulo 'WebSMS' asignado al rol 'Administrador'")
|
||||||
}
|
}
|
||||||
log.Println("[SEED] Seed de WebSMS completado.")
|
log.Println("[SEED] Seed de WebSMS completado.")
|
||||||
@@ -1170,7 +1180,7 @@ func SeedUrlMonitor() {
|
|||||||
if err := db.Where("name = ?", "Administrador").First(&rol).Error; err != nil {
|
if err := db.Where("name = ?", "Administrador").First(&rol).Error; err != nil {
|
||||||
log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
|
log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
|
||||||
} else {
|
} else {
|
||||||
db.Model(&rol).Association("Submodules").Append(&[]models.Submodules{sub})
|
asignarSubmodulosSiFaltan(&rol, []models.Submodules{sub})
|
||||||
log.Printf("[SEED] Submódulo 'Monitor de URLs' asignado al rol 'Administrador'")
|
log.Printf("[SEED] Submódulo 'Monitor de URLs' asignado al rol 'Administrador'")
|
||||||
}
|
}
|
||||||
log.Println("[SEED] Seed de Monitor de URLs completado.")
|
log.Println("[SEED] Seed de Monitor de URLs completado.")
|
||||||
@@ -1206,7 +1216,7 @@ func SeedTareas() {
|
|||||||
if err := db.Where("name = ?", "Administrador").First(&rol).Error; err != nil {
|
if err := db.Where("name = ?", "Administrador").First(&rol).Error; err != nil {
|
||||||
log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
|
log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
|
||||||
} else {
|
} else {
|
||||||
db.Model(&rol).Association("Submodules").Append(&[]models.Submodules{sub})
|
asignarSubmodulosSiFaltan(&rol, []models.Submodules{sub})
|
||||||
log.Printf("[SEED] Submódulo 'Tareas' asignado al rol 'Administrador'")
|
log.Printf("[SEED] Submódulo 'Tareas' asignado al rol 'Administrador'")
|
||||||
}
|
}
|
||||||
log.Println("[SEED] Seed de Tareas completado.")
|
log.Println("[SEED] Seed de Tareas completado.")
|
||||||
|
|||||||
@@ -98,6 +98,31 @@ func GetTables(c *fiber.Ctx) error {
|
|||||||
return c.JSON(fiber.Map{"data": tables})
|
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.
|
// RunQuery ejecuta una consulta SQL.
|
||||||
// POST /app/query-runner/run
|
// POST /app/query-runner/run
|
||||||
// Body: { conx_db_id, database, sql }
|
// Body: { conx_db_id, database, sql }
|
||||||
@@ -113,6 +138,9 @@ func RunQuery(c *fiber.Ctx) error {
|
|||||||
if strings.TrimSpace(body.SQL) == "" {
|
if strings.TrimSpace(body.SQL) == "" {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "La consulta está vacía"})
|
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)))
|
conx, err := loadConxDb(strconv.Itoa(int(body.ConxDbID)))
|
||||||
if err != nil {
|
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"})
|
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)
|
conx, err := loadConxDb(conxDbIDStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
|
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
|
||||||
|
|||||||
+6
-2
@@ -141,8 +141,12 @@ func UserRoutes(app fiber.Router) {
|
|||||||
protected.Get("/query-runner/databases", controllers.GetDatabases)
|
protected.Get("/query-runner/databases", controllers.GetDatabases)
|
||||||
protected.Get("/query-runner/tables", controllers.GetTables)
|
protected.Get("/query-runner/tables", controllers.GetTables)
|
||||||
protected.Get("/query-runner/test", controllers.TestConnection)
|
protected.Get("/query-runner/test", controllers.TestConnection)
|
||||||
protected.Post("/query-runner/run", middlewares.SoloAdmin, controllers.RunQuery)
|
// Sin SoloAdmin a propósito: RunQuery/RunBatchQuery ya limitan el alcance
|
||||||
protected.Post("/query-runner/run-batch", middlewares.SoloAdmin, controllers.RunBatchQuery)
|
// internamente (un no-admin solo ve/consulta las conexiones que su rol
|
||||||
|
// tiene asignadas vía Role.ConxDBs). El acceso real se controla asignando o
|
||||||
|
// no el submódulo "Query Runner" a su rol, no aquí.
|
||||||
|
protected.Post("/query-runner/run", controllers.RunQuery)
|
||||||
|
protected.Post("/query-runner/run-batch", controllers.RunBatchQuery)
|
||||||
protected.Get("/query-runner/history", controllers.GetHistory)
|
protected.Get("/query-runner/history", controllers.GetHistory)
|
||||||
protected.Delete("/query-runner/history", controllers.ClearHistory)
|
protected.Delete("/query-runner/history", controllers.ClearHistory)
|
||||||
protected.Post("/query-runner/export/csv", controllers.ExportCSV)
|
protected.Post("/query-runner/export/csv", controllers.ExportCSV)
|
||||||
|
|||||||
Reference in New Issue
Block a user