diff --git a/VERSION.txt b/VERSION.txt index 3ef6012..a8967ce 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -12,3 +12,4 @@ PROYECTO GIS-GEOSERVER - 2026.04.07.08.18.07 ID DOCKER: d983a409769d. Observacià PROYECTO GIS-GEOSERVER - 2026.04.07.20.37.47 ID DOCKER: d983a409769d. Observación: Modernización definitiva del Dashboard completada: Replicación de bloque de bienvenida legado, integración de mapa embebido en modo limpio y reestructuración de la fila inferior de gestión al 100% de ancho. Versión estable validada.Version SIG (Abril 2026) - 2026.04.07.22.30.00 ID DOCKER: d983a409769d. Observación: Éxito en implementación de Login Dinámico (SaaS), integración de logos binarios, eslóganes y responsables desde el servidor .254. Version SIG (Abril 2026) - 2026.04.07.23.35.00 ID DOCKER: cb7329596324. Observación: Éxito en la implementación de Estadísticas Reales (formato vertical fdw_X.estadisticas_datos) y actualización forzada de FDW desde el menú Administración. Version SIG (Abril 2026) - 2026.04.08.06.45.00 ID DOCKER: cb7329596324. Observación: Inicio de migración a Nomenclatura Alfanumérica Multi-Tenant (e0505_lotes). +Version SIG (Abril 2026) - 2026.04.08.08.15.00 ID DOCKER: cb7329596324. Observación: Consolidación de Arquitectura Multi-Tenant basada en Texto Puro (FDW-TRIM). Blindaje de consultas SQL con filtro de entidad y estandarización alfanumérica de tablas (eXXX_lotes) y vistas (vw_..._XXX). Conexión JOIN optimizada por CCC. diff --git a/src/main/java/com/sigem/gis/security/AuthController.java b/src/main/java/com/sigem/gis/security/AuthController.java index 2768948..5bfc6db 100644 --- a/src/main/java/com/sigem/gis/security/AuthController.java +++ b/src/main/java/com/sigem/gis/security/AuthController.java @@ -45,12 +45,15 @@ public class AuthController { @PostMapping("/login") public ResponseEntity login(@RequestBody AuthRequest request) { try { + // Normalización: Únicamente TRIM para respetar el valor de texto original + String entidadIdDropdown = request.getEntidad() != null ? request.getEntidad().trim() : ""; + // 1. Validar existencia de entidad en directorio maestro (en el .254) String sqlEntidades = "SELECT sigem_site, sigem_dbname, lat, lng, zoom, minzoom, maxzoom, mapa_base, boundno, boundse, nombre, eslogan, entidad_logo, responsable FROM public.entidades WHERE activo= TRUE AND entidad = ?"; - List> entidades = masterJdbcTemplate.queryForList(sqlEntidades, Integer.parseInt(request.getEntidad())); + List> entidades = masterJdbcTemplate.queryForList(sqlEntidades, Integer.parseInt(entidadIdDropdown)); if (entidades.isEmpty()) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new AuthResponse(null, null, "La MUNICIPALIDAD con código " + request.getEntidad() + " no existe o está inactiva.")); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new AuthResponse(null, null, "La MUNICIPALIDAD con código " + entidadIdDropdown + " no existe o está inactiva.")); } Map entidadData = entidades.get(0); @@ -63,11 +66,10 @@ public class AuthController { String responsable = convertObjectToString(entidadData.get("responsable")); - // 2. Asegurar Infraestructura FDW (Regla 16: Solo crea si no existe) - fdwService.setupFdw(request.getEntidad(), false); - String schemaName = "fdw_" + request.getEntidad(); + // 2. Se asume infraestructura FDW básica (Regla 16) + String schemaName = "fdw_" + entidadIdDropdown; - System.out.println("Validando usuario vía FDW local para entidad: " + request.getEntidad()); + System.out.println("Validando usuario vía FDW local para entidad: " + entidadIdDropdown); // 3. Buscar Usuario en el esquema FDW local (en el .123) String sqlUser = "SELECT usu_nom, usu_ape, activo, entidad, " + @@ -84,15 +86,22 @@ public class AuthController { // 4. Validación Final y Generación de Token if (isActivo && request.getPassword().equals(claveDesencriptada)) { - // Extraer la Entidad oficial desde el FDW ( character varying 8 ) + // Extraer la Entidad oficial desde el FDW respetando el texto original - Aplicar TRIM() String entidadOficial = convertObjectToString(userData.get("entidad")); - if (entidadOficial == null || entidadOficial.trim().isEmpty()) { - entidadOficial = request.getEntidad(); // Fallback si el campo está vacío, aunque no debería + if (entidadOficial != null) { + entidadOficial = entidadOficial.trim(); + } + + if (entidadOficial == null || entidadOficial.isEmpty()) { + entidadOficial = entidadIdDropdown; } String token = jwtUtil.generateToken(request.getUsername(), entidadOficial); String nombreCompleto = convertObjectToString(userData.get("usu_nom")) + " " + convertObjectToString(userData.get("usu_ape")); + // 3. ASEGURAR INFRAESTRUCTURA CON IDENTIDAD OFICIAL (Alfanumérica) + fdwService.setupFdw(entidadOficial, false); + // Metadatos georreferenciados de la entidad Double lat = parseDouble(entidadData.get("lat"), -25.456443); Double lng = parseDouble(entidadData.get("lng"), -56.446949); diff --git a/src/main/java/com/sigem/gis/security/AuthResponse.java b/src/main/java/com/sigem/gis/security/AuthResponse.java index a3f80e1..563f58a 100644 --- a/src/main/java/com/sigem/gis/security/AuthResponse.java +++ b/src/main/java/com/sigem/gis/security/AuthResponse.java @@ -17,6 +17,7 @@ public class AuthResponse { private String eslogan; private String entidadLogo; private String responsable; + private String entidad; public AuthResponse(String token, String nombre, String message) { this.token = token; @@ -37,12 +38,13 @@ public class AuthResponse { this.bounds = bounds; } - public AuthResponse(String token, String nombre, String message, Double lat, Double lng, Integer zoom, Integer minZoom, Integer maxZoom, String mapaBase, String bounds, String entidadNombre, String eslogan, String entidadLogo, String responsable) { + public AuthResponse(String token, String nombre, String message, Double lat, Double lng, Integer zoom, Integer minZoom, Integer maxZoom, String mapaBase, String bounds, String entidadNombre, String eslogan, String entidadLogo, String responsable, String entidad) { this(token, nombre, message, lat, lng, zoom, minZoom, maxZoom, mapaBase, bounds); this.entidadNombre = entidadNombre; this.eslogan = eslogan; this.entidadLogo = entidadLogo; this.responsable = responsable; + this.entidad = entidad; } // Getters y Setters @@ -60,4 +62,5 @@ public class AuthResponse { public String getEslogan() { return eslogan; } public String getEntidadLogo() { return entidadLogo; } public String getResponsable() { return responsable; } + public String getEntidad() { return entidad; } } diff --git a/src/main/java/com/sigem/gis/service/FdwService.java b/src/main/java/com/sigem/gis/service/FdwService.java index db62d97..8aaaebe 100644 --- a/src/main/java/com/sigem/gis/service/FdwService.java +++ b/src/main/java/com/sigem/gis/service/FdwService.java @@ -57,15 +57,16 @@ public class FdwService { // ... // Regla Multi-Tenant: Verificar presencia de las 5 tablas críticas String checkSql = "SELECT count(*) FROM information_schema.tables " + - "WHERE table_schema = ? AND table_name IN " + - "('usuarios', 'estadisticas_datos', 'v_liq_entidad_percentiles', 'v_liq_entidad_totalxobjeto', 'ventanas_usuario')"; - + "WHERE table_schema = ? AND table_name IN " + + "('usuarios', 'estadisticas_datos', 'v_liq_entidad_percentiles', 'v_liq_entidad_totalxobjeto', 'ventanas_usuario')"; + Integer count = (forceUpdate) ? 0 : gisJdbcTemplate.queryForObject(checkSql, Integer.class, schemaName); - + // Si falta alguna de las 5 tablas o se solicita actualización forzada if (forceUpdate || count == null || count < 5) { if (count != null && count > 0 && count < 5) { - System.out.println("Infraestructura incompleta para " + entidadId + " (" + count + "/5 tablas). Forzando recreación..."); + System.out.println("Infraestructura incompleta para " + entidadId + " (" + count + + "/5 tablas). Forzando recreación..."); } // (creación de server, user mapping y esquema igual) gisJdbcTemplate.execute("DROP SERVER IF EXISTS " + serverName + " CASCADE"); @@ -90,7 +91,7 @@ public class FdwService { "CREATE OR REPLACE VIEW public.%s AS " + "SELECT l.*, m.inm_ficha, m.inm_ctacatastral, m.trb_total_deuda, m.trb_total_pago, m.ultimo_pago " + "FROM %s l " + - "LEFT JOIN %s.v_liq_entidad_totalxobjeto m ON l.ccc = m.inm_ctacatastral AND l.entidad::text = m.entidad::text", + "LEFT JOIN %s.v_liq_entidad_totalxobjeto m ON l.ccc = m.inm_ctacatastral", viewLotesName, tableLotes, schemaName)); // Vista PNG FULL (WMS) - SIN LIMIT @@ -99,7 +100,7 @@ public class FdwService { "CREATE OR REPLACE VIEW public.%s AS " + "SELECT l.*, m.inm_ficha, m.inm_ctacatastral, m.trb_total_deuda, m.trb_total_pago, m.ultimo_pago " + "FROM %s l " + - "LEFT JOIN %s.v_liq_entidad_totalxobjeto m ON l.ccc = m.inm_ctacatastral AND l.entidad::text = m.entidad::text", + "LEFT JOIN %s.v_liq_entidad_totalxobjeto m ON l.ccc = m.inm_ctacatastral", viewWmsName, tableLotes, schemaName)); // 4. Sincronización con GeoServer