siip-available-ips/src/IpValidator.php
DANYDHSV 5824336ab0 feat: implementar sistema de validación de IPs de 4 capas v1.6.0
- Agregar validación multi-capa para garantizar IPs 100% disponibles
- Capa 1: Filtrar IPs de dispositivos registrados en UISP
- Capa 2: Filtrar IPs de servicios CRM suspendidos/finalizados (449 IPs)
- Capa 3: Validar IPs usando búsqueda de sitios con type=subscriber
- Capa 4: Verificación opcional por ping para dispositivos de terceros

Nuevos Componentes:
- src/IpValidator.php: Clase de validación por búsqueda de sitios
- Validación progresiva en frontend JavaScript
- Endpoint action=validate en public.php

Mejoras:
- CrmService.php: Filtrado mejorado de servicios suspendidos/finalizados
- IpSearchService.php: Extraer todas las IPs de ipAddressList
- Frontend: Validación progresiva de IPs con feedback visual
- Cambiar suspended=false a suspended=true en API de UISP

Correcciones:
- Corregir variable $config indefinida en handler de validación
- Corregir falsos positivos agregando parámetro type=subscriber
- Corregir IPs secundarias no detectadas
- Corregir IPs de servicios suspendidos/finalizados mostrándose disponibles

Resultados de Pruebas:
- IP 172.16.54.70 (servicio finalizado): Filtrada correctamente por capa CRM
- IP 172.16.54.58 (cliente activo): Filtrada correctamente por búsqueda de sitios
- Todas las IPs mostradas verificadas como 100% disponibles

Documentación:
- Actualizado README.md con detalles del sistema de 4 capas
- Actualizado CHANGELOG.md con notas de versión 1.6.0
- Creado walkthrough.md completo
2025-12-06 12:16:28 -06:00

157 lines
5.4 KiB
PHP

<?php
namespace SiipAvailableIps;
/**
* Validador de IPs usando búsqueda de sitios en UISP
* Utiliza el endpoint /sites/search para encontrar dispositivos por IP
*/
class IpValidator
{
private $baseUrl;
private $apiToken;
private $logger;
public function __construct($baseUrl, $apiToken, $logger = null)
{
$this->baseUrl = rtrim($baseUrl, '/');
$this->apiToken = $apiToken;
$this->logger = $logger;
}
/**
* Verifica si una IP está en uso en UISP usando búsqueda de sitios
*
* @param string $ip IP a verificar
* @return bool True si la IP está en uso, false si está disponible
*/
public function isIpInUse($ip)
{
try {
// 1. Buscar sitio por IP usando el endpoint de búsqueda
// IMPORTANTE: type=subscriber solo busca en sitios de clientes (endpoints)
$searchUrl = $this->baseUrl . '/nms/api/v2.1/sites/search?query=' . urlencode($ip) . '&count=1&page=1&type=subscriber';
if ($this->logger) {
$this->logger->appendLog("IpValidator: Buscando sitio para IP $ip");
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $searchUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Auth-Token: ' . $this->apiToken
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
if ($this->logger) {
$this->logger->appendLog("IpValidator: Error HTTP $httpCode al buscar sitio");
}
return false; // Si hay error, asumir que no está en uso
}
$sites = json_decode($response, true);
// Si no hay sitios, la IP no está en uso
if (empty($sites) || !isset($sites[0]['id'])) {
if ($this->logger) {
$this->logger->appendLog("IpValidator: No se encontró sitio para IP $ip - DISPONIBLE");
}
return false;
}
$siteId = $sites[0]['id'];
if ($this->logger) {
$this->logger->appendLog("IpValidator: Sitio encontrado: $siteId");
}
// 2. Obtener dispositivos del sitio
$devicesUrl = $this->baseUrl . '/nms/api/v2.1/devices?siteId=' . $siteId;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $devicesUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Auth-Token: ' . $this->apiToken
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
if ($this->logger) {
$this->logger->appendLog("IpValidator: Error HTTP $httpCode al obtener dispositivos");
}
return false;
}
$devices = json_decode($response, true);
// 3. Verificar si algún dispositivo tiene la IP
foreach ($devices as $device) {
// Verificar IP principal
if (isset($device['ipAddress'])) {
$deviceIp = explode('/', $device['ipAddress'])[0];
if ($deviceIp === $ip) {
if ($this->logger) {
$this->logger->appendLog("IpValidator: IP $ip encontrada en dispositivo (ipAddress) - EN USO");
}
return true;
}
}
// Verificar lista de IPs
if (isset($device['ipAddressList']) && is_array($device['ipAddressList'])) {
if (in_array($ip, $device['ipAddressList'])) {
if ($this->logger) {
$this->logger->appendLog("IpValidator: IP $ip encontrada en dispositivo (ipAddressList) - EN USO");
}
return true;
}
}
}
if ($this->logger) {
$this->logger->appendLog("IpValidator: IP $ip no encontrada en dispositivos del sitio - DISPONIBLE");
}
return false;
} catch (\Exception $e) {
if ($this->logger) {
$this->logger->appendLog("IpValidator: Excepción: " . $e->getMessage());
}
return false; // En caso de error, asumir disponible
}
}
/**
* Valida un lote de IPs y devuelve las que están en uso
*
* @param array $ips Array de IPs a validar
* @return array Array de IPs que están en uso
*/
public function getIpsInUse(array $ips)
{
$ipsInUse = [];
foreach ($ips as $ip) {
if ($this->isIpInUse($ip)) {
$ipsInUse[] = $ip;
}
}
return $ipsInUse;
}
}