Resumen

  • La caché de permisos del cliente PAI publicado por LACNIC tiene una duración de dos minutos. Un fallo de actualización previsto puede reiniciar el plazo y devolver los mismos datos anteriores.
  • La prueba incluida utiliza JSON malformado y espera expresamente esa continuidad. Una respuesta negativa nueva, decodificada correctamente, sí sustituye la entrada anterior.
  • El código no demuestra un acceso revocado que siga funcionando, una instalación en producción ni un ataque. Permite examinar una diferencia concreta entre la edad de la respuesta y la edad aparente de su caché.

La prueba no quiere que el usuario desaparezca

En una prueba de software, lo que se espera suele contar tanto como lo que se ejecuta. El caso testGetTokenDataReturnsCachedDataWhenRefreshFails del cliente público PAI de LACNIC no espera expulsar al usuario cuando llega una respuesta ilegible. Espera recibir el mismo objeto autenticado que había obtenido antes. El autor del cliente está defendiendo continuidad, no describiendo un incidente.

El archivo de pruebas simula primero una respuesta positiva. Después envejece artificialmente la entrada, situando su fecha cinco minutos en el pasado, y entrega JSON malformado en una segunda respuesta simulada. Las comprobaciones exigen el mismo objeto y dos ejecuciones HTTP. Aquí no transcurrieron cinco minutos en un servicio real: se modificó una fecha del escenario de prueba.

Para este artículo se inspeccionaron los archivos; no se ejecutó esa prueba. Tampoco se consultó un punto de acceso de producción ni se emplearon credenciales reales. El escenario sirve para establecer una expectativa del código publicado. No sirve para afirmar que alguien mantuvo acceso tras una revocación.

La continuidad tiene un argumento legítimo. Un fallo al leer una nueva respuesta no demuestra que haya cambiado el derecho de cada usuario. Interrumpir todas las sesiones puede multiplicar el daño de una dependencia temporalmente defectuosa. Sin embargo, mantener un resultado anterior obliga a responder otra pregunta: ¿desde cuándo se está viviendo de esa respuesta y hasta cuándo se acepta hacerlo?

Dos minutos de qué

La implementación examinada pertenece al commit b85523718bfdbd834c85add8e06c3b4a1b813e59, observado en la rama principal del repositorio el 14 de septiembre de 2026. El README presenta un cliente Java para autenticación, autorización y funciones de sesión de PAI. Publicar esa implementación no identifica qué aplicación la ha instalado ni qué comprobaciones añade alrededor de ella.

En PortalWSClient, CACHE_DURATION_MS fija una duración de dos minutos. Una ConcurrentHashMap estática, local al proceso, guarda entradas utilizando como clave el token recibido. Cada entrada contiene TokenData y una marca de tiempo que puede cambiar. El cálculo de expiración compara el tiempo actual con esa marca.

Cuando la entrada todavía no ha expirado, la llamada devuelve sus datos. Ese método no obtiene en esa rama una nueva respuesta de /authorization. Es una caché convencional: evita llamadas repetidas y permite aprovechar un resultado reciente. No hay fundamento para declarar que toda caché de autorización es, por definición, un mecanismo de acceso indebido.

Si ha expirado, el método elimina la entrada del mapa y trata de actualizarla. Pero conserva una referencia local al objeto eliminado. Dejar de figurar en el mapa no equivale a dejar de existir dentro de la llamada que intenta renovarlo. Esa referencia es la reserva a la que puede regresar el tratamiento de errores.

Una nueva respuesta correctamente decodificada genera una entrada nueva. Esto vale también para datos negativos de autorización. La afirmación de que el cliente siempre ignora un rechazo nuevo para mantener un permiso viejo sería falsa. El problema analizado no es un rechazo inteligible: es un fallo concreto al obtener datos que puedan interpretarse por el camino previsto.

En el bloque que captura IOException, la existencia de la referencia anterior permite invocar cached.extend(). La extensión pone la marca de tiempo a la hora actual, reinserta la entrada y devuelve los datos que ya tenía. No hay una decisión nueva en ese paso. Hay dos minutos adicionales para el recipiente de una decisión anterior.

Si vuelve a darse un fallo de esa clase después de otra expiración, el mismo recorrido puede repetirse. La clase no conserva una fecha inmutable de la respuesta original correctamente decodificada, un límite absoluto calculado desde ella ni un contador de extensiones. Es una observación sobre esta clase, no sobre todas las defensas que pueda tener una aplicación que la consume.

Un resultado sin historial de frescura

El objeto TokenData incluye estado de autenticación, token, roles, error e ipAllowed. No ofrece al llamante una fecha de la última respuesta válida, una clasificación de frescura o una señal de que se está devolviendo el valor anterior durante una degradación. Esos campos, por sí solos, no cuentan la edad del permiso conservado.

La distinción tiene importancia incluso si el token sigue dentro de su propia vigencia. La duración criptográfica de un token, la duración renovada de una entrada de caché y la actualidad de un rol en el emisor son tres condiciones distintas. Una no certifica las otras. Tampoco el reloj de la caché indica cuándo debe surtir efecto un cambio administrativo de permisos.

El consumidor puede validar el vencimiento del token, imponer restricciones IP, guardar sus propias fechas o exigir una comprobación adicional antes de una operación sensible. El código no descarta esas posibilidades. Tampoco permite suponerlas. Por eso la conclusión debe permanecer en el contrato que este objeto efectivamente muestra, no en una arquitectura de producción imaginada.

Existe además un aviso de registro cuando se usa temporalmente la caché extendida. No sería correcto afirmar que la continuidad carece de toda señal. Un operador podría detectar ese aviso. La limitación específica es que la edad de la última respuesta no viaja dentro del objeto que recibe quien decide la siguiente operación.

Qué se entiende por una respuesta fiable

La frescura tampoco debería definirse únicamente como «se pudo leer el JSON». PortalHttpClient construye el cliente con TrustAllStrategy y NoopHostnameVerifier. En ese componente, la URL HTTPS y una respuesta decodificada no bastan para demostrar la verificación criptográfica de la identidad del emisor.

Esta es una observación separada y limitada del código. No acredita interceptación, ataque ni la configuración TLS de un despliegue concreto. Su relevancia para el reloj es conceptual: una política de última respuesta fiable debe definir también quién ha autenticado la procedencia de esa respuesta. Una fecha precisa aplicada a una procedencia no comprobada no resuelve toda la decisión de confianza.

También hay que distinguir tipos de fallo. readUrlToken captura excepciones de forma amplia y puede devolver un valor nulo, mientras que el bloque exterior de continuidad captura IOException. No se han ejecutado aquí todos los caminos de transporte, ausencia de contenido o lectura. Un JSON malformado en la prueba no autoriza a convertir cualquier caída de red en una extensión garantizada.

Así, «siempre funciona cuando cae la autorización» exageraría la resiliencia demostrada. «Toda caída deja vigentes permisos revocados» exageraría el riesgo. Ninguna frase respeta el alcance de la evidencia ni las comprobaciones que podría aplicar el consumidor. El resultado respaldado es que la rama probada devuelve el objeto anterior y reinicia el reloj de su entrada.

La gracia necesita fecha y alcance

Una interfaz más explícita podría separar la última respuesta de autorización autenticada y correctamente decodificada de la última actualización del contenedor. Consultar o prolongar la caché no movería la primera fecha. El usuario del cliente sabría si recibió datos frescos, datos aceptados durante una gracia o una indisponibilidad que exige otra decisión.

La gracia podría finalizar en un punto absoluto medido desde la respuesta fiable. Renovarla después de cada intento fallido dejaría de confundirse con extender la actualidad del permiso. Las operaciones podrían tener tolerancias diferentes: mantener una lectura reversible no es necesariamente motivo suficiente para admitir una modificación administrativa irreversible. Son ejemplos analíticos de clases de operación, no una identificación de funciones de LACNIC que usen este código.

Los avisos permitirían vigilar la edad del resultado y la frecuencia de la degradación sin almacenar tokens brutos. Una respuesta fiable nueva cerraría el estado de gracia; una negativa nueva seguiría siendo negativa. Estas son propuestas editoriales, no compromisos publicados de LACNIC ni una afirmación de que sus aplicaciones carecen de controles equivalentes.

La pregunta para un cliente de autorización no es simplemente si dispone de caché. Es si permite reconocer la autoridad que está aplazando y la edad de su última respuesta fiable. Los dos minutos configuran el contenedor. Sin otro reloj, no describen necesariamente la edad máxima de la decisión que contiene.

Fuentes

Las cinco fuentes primarias enlazadas son PortalWSClientTest, el README, PortalWSClient, TokenData y PortalHttpClient, fijadas al mismo commit. La inspección documenta código público y un escenario simulado. No establece ejecución de pruebas, adopción en producción, compromiso de credenciales ni ninguna operación realizada sin autorización.