Files
adif-api-reverse-engineering/test_complete_bodies.py

374 lines
14 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Script de prueba con los REQUEST BODIES COMPLETOS descubiertos
en el análisis de ingeniería reversa del código decompilado.
Incluye el objeto DetailedInfoDTO completo para estaciones.
"""
import requests
import json
import time
from datetime import datetime
# Headers correctos del análisis
HEADERS_CIRCULATION = {
"Content-Type": "application/json;charset=utf-8",
"User-key": "f4ce9fbfa9d721e39b8984805901b5df"
}
HEADERS_STATIONS = {
"Content-Type": "application/json;charset=utf-8",
"User-key": "0d021447a2fd2ac64553674d5a0c1a6f"
}
# URLs base
BASE_CIRCULATION = "https://circulacion.api.adif.es"
BASE_STATIONS = "https://estaciones.api.adif.es"
def test_endpoint(name, method, url, headers, data=None, save_response=False):
"""Probar un endpoint y mostrar resultado detallado"""
print(f"\n{'='*70}")
print(f"TEST: {name}")
print(f"{'='*70}")
print(f"Method: {method}")
print(f"URL: {url}")
print(f"Headers: {json.dumps(headers, indent=2)}")
if data:
print(f"\nRequest Body:")
print(json.dumps(data, indent=2, ensure_ascii=False))
try:
start_time = time.time()
if method == "GET":
response = requests.get(url, headers=headers, timeout=15, verify=True)
elif method == "POST":
response = requests.post(url, headers=headers, json=data, timeout=15, verify=True)
else:
print(f"❌ Método {method} no soportado")
return False
elapsed = time.time() - start_time
print(f"\n⏱️ Tiempo de respuesta: {elapsed:.2f}s")
print(f"📊 Status Code: {response.status_code}")
print(f"📦 Content-Length: {len(response.content)} bytes")
print(f"📋 Response Headers:")
for key, value in response.headers.items():
print(f" {key}: {value}")
if response.status_code == 200:
print("\n✅ SUCCESS - La petición funcionó!")
try:
result = response.json()
resp_str = json.dumps(result, indent=2, ensure_ascii=False)
print(f"\n📄 Response Body (primeros 1500 chars):")
print(resp_str[:1500])
if len(resp_str) > 1500:
print(f"\n... ({len(resp_str) - 1500} caracteres más)")
if save_response:
filename = f"response_{name.replace(' ', '_').replace('/', '_')}.json"
with open(filename, 'w', encoding='utf-8') as f:
json.dump(result, f, indent=2, ensure_ascii=False)
print(f"\n💾 Respuesta guardada en: {filename}")
return True
except json.JSONDecodeError:
print(f"\n⚠️ Respuesta no es JSON válido:")
print(response.text[:500])
return False
elif response.status_code == 401:
print("\n🔒 ERROR 401 - UNAUTHORIZED")
print("Problema de autenticación. Se necesitan headers adicionales.")
print(f"Response: {response.text[:500]}")
return False
elif response.status_code == 403:
print("\n🚫 ERROR 403 - FORBIDDEN")
print("Acceso denegado. Posible problema con User-key o autenticación.")
print(f"Response: {response.text[:500]}")
return False
elif response.status_code == 400:
print("\n❌ ERROR 400 - BAD REQUEST")
print("El formato del body es incorrecto.")
print(f"Response: {response.text[:500]}")
return False
elif response.status_code == 404:
print("\n❌ ERROR 404 - NOT FOUND")
print("El endpoint no existe.")
print(f"Response: {response.text[:500]}")
return False
else:
print(f"\n❌ ERROR {response.status_code}")
print(f"Response: {response.text[:500]}")
return False
except requests.exceptions.Timeout:
print("\n⏱️ ERROR: Timeout - El servidor no respondió a tiempo")
return False
except requests.exceptions.SSLError as e:
print(f"\n🔒 ERROR SSL: {str(e)}")
print("Posible certificate pinning activo en el servidor")
return False
except requests.exceptions.ConnectionError as e:
print(f"\n🌐 ERROR de Conexión: {str(e)}")
return False
except Exception as e:
print(f"\n💥 EXCEPTION: {type(e).__name__}: {str(e)}")
return False
def main():
print("=" * 70)
print("PRUEBAS CON REQUEST BODIES COMPLETOS")
print("Análisis de ingeniería reversa - Código decompilado")
print("=" * 70)
print(f"Fecha: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
results = {}
# =========================================================================
# TEST 1: Detalles de Estación con DetailedInfoDTO COMPLETO
# =========================================================================
print("\n\n" + "🔍 " * 20)
print("TEST 1: Detalles de Estación (DetailedInfoDTO completo)")
print("🔍 " * 20)
# Este es el body COMPLETO descubierto en el código
results['station_details'] = test_endpoint(
"Station Details - Madrid Atocha",
"POST",
f"{BASE_STATIONS}/portroyalmanager/secure/stations/onestation/",
HEADERS_STATIONS,
{
"detailedInfo": {
"extendedStationInfo": True,
"stationActivities": True,
"stationBanner": True,
"stationCommercialServices": True,
"stationInfo": True,
"stationServices": True,
"stationTransportServices": True
},
"stationCode": "10200", # Madrid Atocha
"token": "test_token_12345" # Token de prueba
},
save_response=True
)
# =========================================================================
# TEST 2: Observaciones de Estación
# =========================================================================
print("\n\n" + "🔍 " * 20)
print("TEST 2: Observaciones de Estación")
print("🔍 " * 20)
results['station_observations'] = test_endpoint(
"Station Observations - Multiple Stations",
"POST",
f"{BASE_STATIONS}/portroyalmanager/secure/stationsobservations/",
HEADERS_STATIONS,
{
"stationCodes": ["10200", "10302", "71801"] # Madrid, Madrid, Barcelona
},
save_response=True
)
# =========================================================================
# TEST 3: Salidas/Departures - TrafficCirculationPathRequest completo
# =========================================================================
print("\n\n" + "🔍 " * 20)
print("TEST 3: Salidas/Departures")
print("🔍 " * 20)
results['departures_all'] = test_endpoint(
"Departures - Madrid Atocha (ALL traffic)",
"POST",
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/departures/traffictype/",
HEADERS_CIRCULATION,
{
"commercialService": "BOTH",
"commercialStopType": "BOTH",
"destinationStationCode": None,
"originStationCode": None,
"page": {
"pageNumber": 0
},
"stationCode": "10200",
"trafficType": "ALL"
},
save_response=True
)
# =========================================================================
# TEST 4: Llegadas/Arrivals
# =========================================================================
print("\n\n" + "🔍 " * 20)
print("TEST 4: Llegadas/Arrivals")
print("🔍 " * 20)
results['arrivals_cercanias'] = test_endpoint(
"Arrivals - Madrid Atocha (CERCANIAS)",
"POST",
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/arrivals/traffictype/",
HEADERS_CIRCULATION,
{
"commercialService": "BOTH",
"commercialStopType": "BOTH",
"destinationStationCode": None,
"originStationCode": None,
"page": {
"pageNumber": 0
},
"stationCode": "10200",
"trafficType": "CERCANIAS"
},
save_response=True
)
# =========================================================================
# TEST 5: Entre Estaciones
# =========================================================================
print("\n\n" + "🔍 " * 20)
print("TEST 5: Entre Estaciones")
print("🔍 " * 20)
results['between_stations'] = test_endpoint(
"Between Stations - Madrid to Barcelona",
"POST",
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/betweenstations/traffictype/",
HEADERS_CIRCULATION,
{
"commercialService": "BOTH",
"commercialStopType": "BOTH",
"destinationStationCode": "71801", # Barcelona Sants
"originStationCode": "10200", # Madrid Atocha
"page": {
"pageNumber": 0
},
"stationCode": None,
"trafficType": "ALL"
},
save_response=True
)
# =========================================================================
# TEST 6: Detalles de Ruta - OneOrSeveralPathsRequest
# =========================================================================
print("\n\n" + "🔍 " * 20)
print("TEST 6: Detalles de Ruta Específica")
print("🔍 " * 20)
# Timestamp para hoy a las 00:00
today_timestamp = int(datetime.now().replace(hour=0, minute=0, second=0, microsecond=0).timestamp() * 1000)
results['onepaths'] = test_endpoint(
"OnePaths - Madrid to Barcelona",
"POST",
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpathdetails/onepaths/",
HEADERS_CIRCULATION,
{
"allControlPoints": True,
"commercialNumber": None,
"destinationStationCode": "71801",
"launchingDate": today_timestamp, # Timestamp en milisegundos
"originStationCode": "10200"
},
save_response=True
)
# =========================================================================
# TEST 7: Composiciones de Tren
# =========================================================================
print("\n\n" + "🔍 " * 20)
print("TEST 7: Composiciones de Tren")
print("🔍 " * 20)
results['compositions'] = test_endpoint(
"Train Compositions",
"POST",
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/compositions/path/",
HEADERS_CIRCULATION,
{
"allControlPoints": False,
"commercialNumber": None,
"destinationStationCode": "71801",
"launchingDate": None,
"originStationCode": "10200"
},
save_response=True
)
# =========================================================================
# TEST 8: Salidas con diferentes TrafficTypes
# =========================================================================
print("\n\n" + "🔍 " * 20)
print("TEST 8: Diferentes TrafficTypes")
print("🔍 " * 20)
for traffic_type in ["AVLDMD", "TRAVELERS", "GOODS", "OTHERS"]:
results[f'departures_{traffic_type.lower()}'] = test_endpoint(
f"Departures - TrafficType={traffic_type}",
"POST",
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/departures/traffictype/",
HEADERS_CIRCULATION,
{
"commercialService": "BOTH",
"commercialStopType": "BOTH",
"page": {"pageNumber": 0},
"stationCode": "10200",
"trafficType": traffic_type
}
)
# =========================================================================
# RESUMEN FINAL
# =========================================================================
print("\n\n" + "="*70)
print("📊 RESUMEN DE PRUEBAS")
print("="*70)
total = len(results)
passed = sum(1 for v in results.values() if v)
failed = total - passed
print(f"\n📈 Estadísticas:")
print(f" Total de pruebas: {total}")
print(f" ✅ Exitosas: {passed}")
print(f" ❌ Fallidas: {failed}")
print(f" 📊 Tasa de éxito: {(passed/total*100):.1f}%")
print(f"\n📋 Detalle por prueba:")
for test_name, result in results.items():
status = "✅ PASS" if result else "❌ FAIL"
print(f" {status} - {test_name}")
print("\n" + "="*70)
if passed == total:
print("🎉 ¡ÉXITO TOTAL! Todas las pruebas pasaron.")
print("Los request bodies son correctos y el servidor los acepta.")
elif passed > 0:
print(f"⚠️ ÉXITO PARCIAL: {passed}/{total} pruebas funcionaron.")
print("\nLas pruebas fallidas probablemente requieren:")
print(" - Headers adicionales de autenticación (X-CanalMovil-*)")
print(" - Token válido generado por el sistema de autenticación HMAC")
print("\nVer API_REQUEST_BODIES.md sección 5 para más detalles.")
else:
print("❌ TODAS LAS PRUEBAS FALLARON")
print("\nPosibles causas:")
print(" 1. Sistema de autenticación HMAC-SHA256 requerido")
print(" 2. Headers X-CanalMovil-* faltantes")
print(" 3. Certificate pinning activo")
print(" 4. Servidor requiere User-Agent específico")
print("\nConsultar README.md sección 'Sistema de Autenticación'")
print("="*70 + "\n")
if __name__ == "__main__":
main()