Agregados varios //TODO para revisar
This commit is contained in:
@@ -72,6 +72,16 @@ Body:
|
||||
POST /portroyalmanager/secure/stationsobservations/
|
||||
Base: https://estaciones.api.adif.es
|
||||
Headers: User-key para estaciones
|
||||
|
||||
Body:
|
||||
{
|
||||
"stationCodes": ["string"] // Array de códigos de estación (requerido)
|
||||
}
|
||||
|
||||
Ejemplo:
|
||||
{
|
||||
"stationCodes": ["60000", "71801"]
|
||||
}
|
||||
```
|
||||
|
||||
### Circulaciones (Trenes)
|
||||
@@ -84,16 +94,28 @@ Headers: User-key para circulaciones
|
||||
|
||||
Body:
|
||||
{
|
||||
"commercialService": "YES|NO|ALL",
|
||||
"commercialStopType": "YES|NO|ALL",
|
||||
"destinationStationCode": "string|null",
|
||||
"originStationCode": "string|null",
|
||||
"commercialService": "YES|NOT|BOTH", // Estado del servicio comercial
|
||||
"commercialStopType": "YES|NOT|BOTH", // Tipo de parada comercial
|
||||
"destinationStationCode": "string|null", // Código estación destino (opcional)
|
||||
"originStationCode": "string|null", // Código estación origen (opcional)
|
||||
"page": {
|
||||
"page": number,
|
||||
"size": number
|
||||
"pageNumber": number // Número de página
|
||||
},
|
||||
"stationCode": "string|null",
|
||||
"trafficType": "CERCANIAS|MEDIA_DISTANCIA|LARGA_DISTANCIA|ALL"
|
||||
"stationCode": "string|null", // Código estación (opcional)
|
||||
"trafficType": "CERCANIAS|AVLDMD|OTHERS|TRAVELERS|GOODS|ALL" // Tipo de tráfico
|
||||
}
|
||||
|
||||
Ejemplo:
|
||||
{
|
||||
"commercialService": "BOTH",
|
||||
"commercialStopType": "BOTH",
|
||||
"destinationStationCode": null,
|
||||
"originStationCode": null,
|
||||
"page": {
|
||||
"pageNumber": 0
|
||||
},
|
||||
"stationCode": "60000",
|
||||
"trafficType": "ALL"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -102,7 +124,8 @@ Body:
|
||||
POST /portroyalmanager/secure/circulationpaths/arrivals/traffictype/
|
||||
Base: https://circulacion.api.adif.es
|
||||
Headers: User-key para circulaciones
|
||||
Body: Same as departures
|
||||
|
||||
Body: Mismo formato que departures (TrafficCirculationPathRequest)
|
||||
```
|
||||
|
||||
#### Entre estaciones
|
||||
@@ -110,7 +133,8 @@ Body: Same as departures
|
||||
POST /portroyalmanager/secure/circulationpaths/betweenstations/traffictype/
|
||||
Base: https://circulacion.api.adif.es
|
||||
Headers: User-key para circulaciones
|
||||
Body: Same as departures
|
||||
|
||||
Body: Mismo formato que departures (TrafficCirculationPathRequest)
|
||||
```
|
||||
|
||||
#### Una ruta específica
|
||||
@@ -121,11 +145,20 @@ Headers: User-key para circulaciones
|
||||
|
||||
Body:
|
||||
{
|
||||
"allControlPoints": boolean|null,
|
||||
"commercialNumber": "string|null",
|
||||
"destinationStationCode": "string|null",
|
||||
"launchingDate": timestamp|null,
|
||||
"originStationCode": "string|null"
|
||||
"allControlPoints": boolean|null, // Todos los puntos de control (opcional)
|
||||
"commercialNumber": "string|null", // Número comercial del tren (opcional)
|
||||
"destinationStationCode": "string|null", // Código estación destino (opcional)
|
||||
"launchingDate": number|null, // Fecha de lanzamiento en timestamp (Long) (opcional)
|
||||
"originStationCode": "string|null" // Código estación origen (opcional)
|
||||
}
|
||||
|
||||
Ejemplo:
|
||||
{
|
||||
"allControlPoints": true,
|
||||
"commercialNumber": "04138",
|
||||
"destinationStationCode": "60000",
|
||||
"launchingDate": 1733356800000,
|
||||
"originStationCode": "71801"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -134,7 +167,8 @@ Body:
|
||||
POST /portroyalmanager/secure/circulationpathdetails/severalpaths/
|
||||
Base: https://circulacion.api.adif.es
|
||||
Headers: User-key para circulaciones
|
||||
Body: Same as onepaths
|
||||
|
||||
Body: Mismo formato que onepaths (OneOrSeveralPathsRequest)
|
||||
```
|
||||
|
||||
### Composiciones
|
||||
@@ -215,20 +249,21 @@ Headers: Basic auth + X-CanalMovil headers
|
||||
|
||||
### TrafficType (Tipos de tráfico)
|
||||
- `CERCANIAS` - Trenes de cercanías
|
||||
- `MEDIA_DISTANCIA` - Media distancia
|
||||
- `LARGA_DISTANCIA` - Larga distancia
|
||||
- `AVLDMD` - Alta Velocidad, Larga y Media Distancia
|
||||
- `OTHERS` - Otros tipos de tráfico
|
||||
- `TRAVELERS` - Viajeros
|
||||
- `GOODS` - Mercancías
|
||||
- `ALL` - Todos los tipos
|
||||
|
||||
### State (Estados)
|
||||
### State (Estados para comercialService y comercialStopType)
|
||||
- `YES` - Sí
|
||||
- `NO` - No
|
||||
- `ALL` - Todos
|
||||
- `NOT` - No
|
||||
- `BOTH` - Ambos
|
||||
|
||||
### PageInfoDTO
|
||||
```json
|
||||
{
|
||||
"page": 0,
|
||||
"size": 20
|
||||
"pageNumber": 0
|
||||
}
|
||||
```
|
||||
|
||||
@@ -239,9 +274,13 @@ Headers: Basic auth + X-CanalMovil headers
|
||||
- Las User-keys son diferentes para cada servicio (estaciones vs circulaciones)
|
||||
- El token de registro `b9034774-c6e4-4663-a1a8-74bf7102651b` está en el código
|
||||
|
||||
[CODE] 200
|
||||
[METHOD] POST
|
||||
[URL] https://circulacion.api.adif.es/portroyalmanager/secure/circulationpathdetails/onepaths/
|
||||
[URL] https://circulacion.api.adif.es/portroyalmanager/secure/circulationpaths/betweenstations/traffictype/
|
||||
[URL] https://circulacion.api.adif.es/portroyalmanager/secure/circulationpaths/departures/traffictype/
|
||||
[URL] https://estaciones.api.adif.es/portroyalmanager/secure/stationsobservations/
|
||||
## Notas de Implementación
|
||||
|
||||
Esta documentación se ha obtenido mediante ingeniería reversa del código decompilado de la aplicación Android de ADIF Elcano.
|
||||
|
||||
Clases principales analizadas:
|
||||
- `com.adif.elcanomovil.serviceNetworking.circulations.model.request.TrafficCirculationPathRequest`
|
||||
- `com.adif.elcanomovil.serviceNetworking.circulations.model.request.OneOrSeveralPathsRequest`
|
||||
- `com.adif.elcanomovil.serviceNetworking.stationObservations.model.StationObservationsRequest`
|
||||
- `com.adif.elcanomovil.serviceNetworking.circulations.model.request.CirculationPathRequest` (interface)
|
||||
- `com.adif.elcanomovil.serviceNetworking.circulations.model.request.TrafficType` (enum)
|
||||
|
||||
@@ -4,10 +4,11 @@ import java.util.List;
|
||||
import kotlin.Metadata;
|
||||
import kotlin.jvm.internal.Intrinsics;
|
||||
|
||||
// TODO
|
||||
@Metadata(d1 = {"\u0000L\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010 \n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0002\b\u0018\n\u0002\u0010\u000b\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\u0002\b\u0086\b\u0018\u00002\u00020\u0001Bk\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\b\u0010\u0004\u001a\u0004\u0018\u00010\u0005\u0012\b\u0010\u0006\u001a\u0004\u0018\u00010\u0007\u0012\u000e\u0010\b\u001a\n\u0012\u0004\u0012\u00020\n\u0018\u00010\t\u0012\u000e\u0010\u000b\u001a\n\u0012\u0004\u0012\u00020\f\u0018\u00010\t\u0012\u000e\u0010\r\u001a\n\u0012\u0004\u0012\u00020\u000e\u0018\u00010\t\u0012\u000e\u0010\u000f\u001a\n\u0012\u0004\u0012\u00020\u000e\u0018\u00010\t\u0012\b\u0010\u0010\u001a\u0004\u0018\u00010\u0011¢\u0006\u0002\u0010\u0012J\t\u0010 \u001a\u00020\u0003HÆ\u0003J\u000b\u0010!\u001a\u0004\u0018\u00010\u0005HÆ\u0003J\u000b\u0010\"\u001a\u0004\u0018\u00010\u0007HÆ\u0003J\u0011\u0010#\u001a\n\u0012\u0004\u0012\u00020\n\u0018\u00010\tHÆ\u0003J\u0011\u0010$\u001a\n\u0012\u0004\u0012\u00020\f\u0018\u00010\tHÆ\u0003J\u0011\u0010%\u001a\n\u0012\u0004\u0012\u00020\u000e\u0018\u00010\tHÆ\u0003J\u0011\u0010&\u001a\n\u0012\u0004\u0012\u00020\u000e\u0018\u00010\tHÆ\u0003J\u000b\u0010'\u001a\u0004\u0018\u00010\u0011HÆ\u0003J\u007f\u0010(\u001a\u00020\u00002\b\b\u0002\u0010\u0002\u001a\u00020\u00032\n\b\u0002\u0010\u0004\u001a\u0004\u0018\u00010\u00052\n\b\u0002\u0010\u0006\u001a\u0004\u0018\u00010\u00072\u0010\b\u0002\u0010\b\u001a\n\u0012\u0004\u0012\u00020\n\u0018\u00010\t2\u0010\b\u0002\u0010\u000b\u001a\n\u0012\u0004\u0012\u00020\f\u0018\u00010\t2\u0010\b\u0002\u0010\r\u001a\n\u0012\u0004\u0012\u00020\u000e\u0018\u00010\t2\u0010\b\u0002\u0010\u000f\u001a\n\u0012\u0004\u0012\u00020\u000e\u0018\u00010\t2\n\b\u0002\u0010\u0010\u001a\u0004\u0018\u00010\u0011HÆ\u0001J\u0013\u0010)\u001a\u00020*2\b\u0010+\u001a\u0004\u0018\u00010\u0001HÖ\u0003J\t\u0010,\u001a\u00020-HÖ\u0001J\t\u0010.\u001a\u00020\u0003HÖ\u0001R\u0013\u0010\u0010\u001a\u0004\u0018\u00010\u0011¢\u0006\b\n\u0000\u001a\u0004\b\u0013\u0010\u0014R\u0013\u0010\u0006\u001a\u0004\u0018\u00010\u0007¢\u0006\b\n\u0000\u001a\u0004\b\u0015\u0010\u0016R\u0019\u0010\u000f\u001a\n\u0012\u0004\u0012\u00020\u000e\u0018\u00010\t¢\u0006\b\n\u0000\u001a\u0004\b\u0017\u0010\u0018R\u0011\u0010\u0002\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\u0019\u0010\u001aR\u0019\u0010\r\u001a\n\u0012\u0004\u0012\u00020\u000e\u0018\u00010\t¢\u0006\b\n\u0000\u001a\u0004\b\u001b\u0010\u0018R\u0013\u0010\u0004\u001a\u0004\u0018\u00010\u0005¢\u0006\b\n\u0000\u001a\u0004\b\u001c\u0010\u001dR\u0019\u0010\b\u001a\n\u0012\u0004\u0012\u00020\n\u0018\u00010\t¢\u0006\b\n\u0000\u001a\u0004\b\u001e\u0010\u0018R\u0019\u0010\u000b\u001a\n\u0012\u0004\u0012\u00020\f\u0018\u00010\t¢\u0006\b\n\u0000\u001a\u0004\b\u001f\u0010\u0018¨\u0006/"}, d2 = {"Lcom/adif/elcanomovil/domain/entities/station/RequestedStationInfo;", "", "stationCode", "", "stationInfo", "Lcom/adif/elcanomovil/domain/entities/station/StationInfo;", "extendedStationInfo", "Lcom/adif/elcanomovil/domain/entities/station/ExtendedStationInfo;", "stationServices", "", "Lcom/adif/elcanomovil/domain/entities/station/StationServices;", "stationTransportServices", "Lcom/adif/elcanomovil/domain/entities/station/StationTransportServices;", "stationCommercialServices", "Lcom/adif/elcanomovil/domain/entities/station/StationCommercialServices;", "stationActivities", "banner", "Lcom/adif/elcanomovil/domain/entities/station/Banner;", "(Ljava/lang/String;Lcom/adif/elcanomovil/domain/entities/station/StationInfo;Lcom/adif/elcanomovil/domain/entities/station/ExtendedStationInfo;Ljava/util/List;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lcom/adif/elcanomovil/domain/entities/station/Banner;)V", "getBanner", "()Lcom/adif/elcanomovil/domain/entities/station/Banner;", "getExtendedStationInfo", "()Lcom/adif/elcanomovil/domain/entities/station/ExtendedStationInfo;", "getStationActivities", "()Ljava/util/List;", "getStationCode", "()Ljava/lang/String;", "getStationCommercialServices", "getStationInfo", "()Lcom/adif/elcanomovil/domain/entities/station/StationInfo;", "getStationServices", "getStationTransportServices", "component1", "component2", "component3", "component4", "component5", "component6", "component7", "component8", "copy", "equals", "", "other", "hashCode", "", "toString", "domain_proNon_corporateRelease"}, k = 1, mv = {1, 9, 0}, xi = 48)
|
||||
/* loaded from: classes.dex */
|
||||
public final /* data */ class RequestedStationInfo {
|
||||
private final Banner banner;
|
||||
private final Banner banner;StationService
|
||||
private final ExtendedStationInfo extendedStationInfo;
|
||||
private final List<StationCommercialServices> stationActivities;
|
||||
private final String stationCode;
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.adif.elcanomovil.serviceNetworking;
|
||||
import com.adif.elcanomovil.commonNavGraph.arguments.NavArguments;
|
||||
import com.google.firebase.analytics.FirebaseAnalytics;
|
||||
import kotlin.Metadata;
|
||||
|
||||
//TODO
|
||||
@Metadata(d1 = {"\u0000\f\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u000b\bÆ\u0002\u0018\u00002\u00020\u0001:\t\u0003\u0004\u0005\u0006\u0007\b\t\n\u000bB\u0007\b\u0002¢\u0006\u0002\u0010\u0002¨\u0006\f"}, d2 = {"Lcom/adif/elcanomovil/serviceNetworking/ServicePaths;", "", "()V", "AvisaLoginService", "AvisaStationService", "CirculationService", "CompositionService", "Headers", "IncidenceService", "StationObservationsService", "StationService", "SubscriptionsService", "service-networking_proNon_corporateRelease"}, k = 1, mv = {1, 9, 0}, xi = 48)
|
||||
/* loaded from: classes.dex */
|
||||
public final class ServicePaths {
|
||||
|
||||
@@ -1,36 +1,77 @@
|
||||
/**
|
||||
* Capture REQUEST BODY using writeTo() method
|
||||
* Capture REQUEST BODY by hooking MoshiRequestBodyConverter
|
||||
*/
|
||||
|
||||
console.log("\n[*] Capturing REQUEST Bodies\n");
|
||||
console.log("\n[*] Capturing REQUEST Bodies via MoshiRequestBodyConverter\n");
|
||||
|
||||
Java.perform(function() {
|
||||
|
||||
// Hook MoshiRequestBodyConverter.convert() directly
|
||||
try {
|
||||
var MoshiRequestBodyConverter = Java.use("retrofit2.converter.moshi.MoshiRequestBodyConverter");
|
||||
console.log("[+] Found MoshiRequestBodyConverter");
|
||||
|
||||
var convertOriginal = MoshiRequestBodyConverter.convert.overload('java.lang.Object');
|
||||
|
||||
convertOriginal.implementation = function(obj) {
|
||||
// BEFORE calling original, serialize the object ourselves to capture it
|
||||
try {
|
||||
// Get the adapter field to serialize the object
|
||||
var adapterField = this.getClass().getDeclaredField("adapter");
|
||||
adapterField.setAccessible(true);
|
||||
var adapter = adapterField.get(this);
|
||||
|
||||
// Create our own buffer and writer to capture the JSON
|
||||
var Buffer = Java.use("r3.f");
|
||||
var tempBuffer = Buffer.$new();
|
||||
|
||||
// Create JsonWriter with buffer
|
||||
var JsonWriter = Java.use("Z2.t");
|
||||
var JsonWriterConstructor = JsonWriter.class.getDeclaredConstructor([Java.use("r3.i").class]);
|
||||
JsonWriterConstructor.setAccessible(true);
|
||||
var tempWriter = JsonWriterConstructor.newInstance([tempBuffer]);
|
||||
|
||||
// Serialize to our buffer
|
||||
adapter.toJson(tempWriter, obj);
|
||||
tempWriter.close();
|
||||
|
||||
// Read the JSON
|
||||
var jsonContent = tempBuffer.B0(); // readUtf8()
|
||||
|
||||
console.log("\n" + "=".repeat(80));
|
||||
console.log("[CAPTURED REQUEST BODY]");
|
||||
if (jsonContent && jsonContent.length > 0) {
|
||||
if (jsonContent.length > 3000) {
|
||||
console.log(jsonContent.substring(0, 3000));
|
||||
console.log("\n... (truncated, total: " + jsonContent.length + " chars)");
|
||||
} else {
|
||||
console.log(jsonContent);
|
||||
}
|
||||
} else {
|
||||
console.log("(empty)");
|
||||
}
|
||||
console.log("=".repeat(80) + "\n");
|
||||
|
||||
} catch (e) {
|
||||
console.log("[CAPTURE ERROR] " + e);
|
||||
}
|
||||
|
||||
// Call original to return the actual RequestBody
|
||||
return convertOriginal.call(this, obj);
|
||||
};
|
||||
|
||||
console.log("[*] MoshiRequestBodyConverter hook installed!\n");
|
||||
|
||||
} catch (e) {
|
||||
console.log("[-] Failed to hook MoshiRequestBodyConverter: " + e);
|
||||
}
|
||||
|
||||
// Also hook the Auth interceptor to show URLs
|
||||
try {
|
||||
var AuthHeaderInterceptor = Java.use("com.adif.elcanomovil.serviceNetworking.interceptors.AuthHeaderInterceptor");
|
||||
console.log("[+] Found AuthHeaderInterceptor");
|
||||
|
||||
// Try to find Buffer class
|
||||
var Buffer = null;
|
||||
var bufferNames = ["r.f", "r3.f", "okio.Buffer", "r3.Buffer"];
|
||||
for (var i = 0; i < bufferNames.length; i++) {
|
||||
try {
|
||||
Buffer = Java.use(bufferNames[i]);
|
||||
console.log("[+] Found Buffer class: " + bufferNames[i]);
|
||||
break;
|
||||
} catch (e) {
|
||||
// Try next
|
||||
}
|
||||
}
|
||||
|
||||
if (!Buffer) {
|
||||
console.log("[-] Could not find Buffer class, trying without pre-loading");
|
||||
}
|
||||
|
||||
AuthHeaderInterceptor.intercept.implementation = function(chain) {
|
||||
console.log("\n" + "=".repeat(80));
|
||||
console.log("[HTTP REQUEST]");
|
||||
|
||||
try {
|
||||
// Cast chain
|
||||
var ChainClass = Java.use("j3.g");
|
||||
@@ -46,87 +87,26 @@ Java.perform(function() {
|
||||
var urlField = request.getClass().getDeclaredField("a");
|
||||
urlField.setAccessible(true);
|
||||
var urlObj = urlField.get(request);
|
||||
console.log("[URL] " + urlObj.toString());
|
||||
|
||||
// Get method
|
||||
var methodField = request.getClass().getDeclaredField("b");
|
||||
methodField.setAccessible(true);
|
||||
var method = methodField.get(request);
|
||||
console.log("[METHOD] " + method);
|
||||
|
||||
// Get request body
|
||||
var bodyField = request.getClass().getDeclaredField("d");
|
||||
bodyField.setAccessible(true);
|
||||
var reqBody = bodyField.get(request);
|
||||
|
||||
if (reqBody) {
|
||||
try {
|
||||
// If Buffer wasn't found, try to load it now
|
||||
if (!Buffer) {
|
||||
var bufferNames = ["r.f", "r3.f", "okio.Buffer", "r3.Buffer"];
|
||||
for (var i = 0; i < bufferNames.length; i++) {
|
||||
try {
|
||||
Buffer = Java.use(bufferNames[i]);
|
||||
break;
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (Buffer) {
|
||||
// Create a temporary buffer
|
||||
var buffer = Buffer.$new();
|
||||
|
||||
// Try to cast buffer to BufferedSink if needed
|
||||
try {
|
||||
var BufferedSink = Java.use("r3.i");
|
||||
var sink = Java.cast(buffer, BufferedSink);
|
||||
|
||||
// Call writeTo passing the sink
|
||||
reqBody.writeTo(sink);
|
||||
} catch (e) {
|
||||
// If cast fails, try direct call
|
||||
reqBody.writeTo(buffer);
|
||||
}
|
||||
|
||||
// Read the content as UTF-8 string
|
||||
var bodyContent = buffer.B0(); // readUtf8()
|
||||
|
||||
console.log("\n[REQUEST BODY]");
|
||||
if (bodyContent && bodyContent.length > 0) {
|
||||
if (bodyContent.length > 2000) {
|
||||
console.log(bodyContent.substring(0, 2000));
|
||||
console.log("\n... (truncated, total: " + bodyContent.length + " chars)");
|
||||
} else {
|
||||
console.log(bodyContent);
|
||||
}
|
||||
} else {
|
||||
console.log("(empty)");
|
||||
}
|
||||
} else {
|
||||
console.log("\n[REQUEST BODY] Could not load Buffer class");
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log("[REQUEST BODY ERROR] " + e);
|
||||
}
|
||||
} else {
|
||||
console.log("[REQUEST BODY] null");
|
||||
}
|
||||
console.log("\n[REQUEST] " + method + " " + urlObj.toString());
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log("[ERROR] " + e);
|
||||
console.log("[URL CAPTURE ERROR] " + e);
|
||||
}
|
||||
|
||||
console.log("=".repeat(80) + "\n");
|
||||
|
||||
// Call original
|
||||
return this.intercept(chain);
|
||||
};
|
||||
|
||||
console.log("[*] Hook installed!\n");
|
||||
console.log("[*] Interceptor hook installed!\n");
|
||||
|
||||
} catch (e) {
|
||||
console.log("[-] Failed: " + e);
|
||||
console.log("[-] Failed to hook AuthHeaderInterceptor: " + e);
|
||||
}
|
||||
});
|
||||
|
||||
130
frida_scripts/frida_improved_capture.js
Normal file
130
frida_scripts/frida_improved_capture.js
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* Improved REQUEST BODY Capture
|
||||
* Using correct method names discovered through inspection
|
||||
*/
|
||||
|
||||
console.log("\n[*] Improved Request Body Capture\n");
|
||||
|
||||
Java.perform(function() {
|
||||
|
||||
try {
|
||||
var AuthHeaderInterceptor = Java.use("com.adif.elcanomovil.serviceNetworking.interceptors.AuthHeaderInterceptor");
|
||||
console.log("[+] Found AuthHeaderInterceptor");
|
||||
|
||||
AuthHeaderInterceptor.intercept.implementation = function(chain) {
|
||||
console.log("\n" + "=".repeat(80));
|
||||
console.log("[HTTP REQUEST]");
|
||||
|
||||
try {
|
||||
// Cast chain
|
||||
var ChainClass = Java.use("j3.g");
|
||||
var chainObj = Java.cast(chain, ChainClass);
|
||||
|
||||
// Get request
|
||||
var requestField = chainObj.getClass().getDeclaredField("e");
|
||||
requestField.setAccessible(true);
|
||||
var request = requestField.get(chainObj);
|
||||
|
||||
if (request) {
|
||||
// Get URL
|
||||
var urlField = request.getClass().getDeclaredField("a");
|
||||
urlField.setAccessible(true);
|
||||
var urlObj = urlField.get(request);
|
||||
console.log("[URL] " + urlObj.toString());
|
||||
|
||||
// Get method
|
||||
var methodField = request.getClass().getDeclaredField("b");
|
||||
methodField.setAccessible(true);
|
||||
var method = methodField.get(request);
|
||||
console.log("[METHOD] " + method);
|
||||
|
||||
// Get request headers
|
||||
try {
|
||||
var headersField = request.getClass().getDeclaredField("c");
|
||||
headersField.setAccessible(true);
|
||||
var headers = headersField.get(request);
|
||||
|
||||
if (headers) {
|
||||
console.log("\n[REQUEST HEADERS]");
|
||||
var size = headers.size();
|
||||
for (var i = 0; i < size; i++) {
|
||||
var name = headers.c(i);
|
||||
var value = headers.f(i);
|
||||
console.log(" " + name + ": " + value);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("[HEADERS ERROR] " + e);
|
||||
}
|
||||
|
||||
// Get request body
|
||||
var bodyField = request.getClass().getDeclaredField("d");
|
||||
bodyField.setAccessible(true);
|
||||
var reqBody = bodyField.get(request);
|
||||
|
||||
if (reqBody) {
|
||||
try {
|
||||
// Load Buffer class - we know it's r3.f from inspection
|
||||
var Buffer = Java.use("r3.f");
|
||||
var buffer = Buffer.$new();
|
||||
|
||||
// Call writeTo with the buffer (buffer implements BufferedSink)
|
||||
reqBody.writeTo(buffer);
|
||||
|
||||
// Try to read using readUtf8
|
||||
try {
|
||||
var bodyContent = buffer.B0(); // readUtf8()
|
||||
|
||||
console.log("\n[REQUEST BODY]");
|
||||
if (bodyContent && bodyContent.length > 0) {
|
||||
if (bodyContent.length > 3000) {
|
||||
console.log(bodyContent.substring(0, 3000));
|
||||
console.log("\n... (truncated, total: " + bodyContent.length + " chars)");
|
||||
} else {
|
||||
console.log(bodyContent);
|
||||
}
|
||||
} else {
|
||||
console.log("(empty)");
|
||||
}
|
||||
} catch (e) {
|
||||
// If B0() doesn't work, try other common method names
|
||||
console.log("[READ ERROR] " + e);
|
||||
console.log("[DEBUG] Trying alternative methods...");
|
||||
|
||||
try {
|
||||
// Try snapshot().utf8()
|
||||
var snapshot = buffer.t0(); // snapshot()
|
||||
if (snapshot) {
|
||||
var bodyContent = snapshot.Y(); // utf8()
|
||||
console.log("\n[REQUEST BODY]");
|
||||
console.log(bodyContent);
|
||||
}
|
||||
} catch (e2) {
|
||||
console.log("[ALT METHOD ERROR] " + e2);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log("[REQUEST BODY ERROR] " + e);
|
||||
}
|
||||
} else {
|
||||
console.log("[REQUEST BODY] null");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log("[ERROR] " + e);
|
||||
}
|
||||
|
||||
console.log("=".repeat(80) + "\n");
|
||||
|
||||
// Call original
|
||||
return this.intercept(chain);
|
||||
};
|
||||
|
||||
console.log("[*] Hook installed!\n");
|
||||
|
||||
} catch (e) {
|
||||
console.log("[-] Failed: " + e);
|
||||
}
|
||||
});
|
||||
68
frida_scripts/frida_okhttp_intercept.js
Normal file
68
frida_scripts/frida_okhttp_intercept.js
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Intercept at OkHttp level to capture request bodies
|
||||
*/
|
||||
|
||||
console.log("\n[*] OkHttp Request Interceptor\n");
|
||||
|
||||
Java.perform(function() {
|
||||
|
||||
// Hook the RealCall.execute method which actually sends the request
|
||||
try {
|
||||
var RealCall = Java.use("i3.j"); // OkHttp's RealCall
|
||||
console.log("[+] Found RealCall");
|
||||
|
||||
RealCall.g.implementation = function(chain) {
|
||||
console.log("\n" + "=".repeat(80));
|
||||
console.log("[HTTP REQUEST INTERCEPTED]");
|
||||
|
||||
try {
|
||||
// Get the request from chain
|
||||
var request = chain.b();
|
||||
|
||||
if (request) {
|
||||
console.log("[URL] " + request.g().toString());
|
||||
console.log("[METHOD] " + request.f());
|
||||
|
||||
// Get the body
|
||||
var body = request.d();
|
||||
|
||||
if (body) {
|
||||
try {
|
||||
var Buffer = Java.use("r3.f");
|
||||
var buffer = Buffer.$new();
|
||||
|
||||
// Write body to buffer
|
||||
body.writeTo(buffer);
|
||||
|
||||
// Read as string
|
||||
var bodyStr = buffer.B0();
|
||||
|
||||
console.log("\n[REQUEST BODY]");
|
||||
if (bodyStr && bodyStr.length > 0) {
|
||||
console.log(bodyStr);
|
||||
} else {
|
||||
console.log("(empty)");
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("[BODY ERROR] " + e);
|
||||
}
|
||||
} else {
|
||||
console.log("[BODY] null");
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("[ERROR] " + e);
|
||||
}
|
||||
|
||||
console.log("=".repeat(80) + "\n");
|
||||
|
||||
// Call original
|
||||
return this.g(chain);
|
||||
};
|
||||
|
||||
console.log("[*] Hook installed!\n");
|
||||
|
||||
} catch (e) {
|
||||
console.log("[-] Failed to hook RealCall: " + e);
|
||||
}
|
||||
});
|
||||
118
frida_scripts/frida_reflection_capture.js
Normal file
118
frida_scripts/frida_reflection_capture.js
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Request Body Capture using Reflection
|
||||
* Automatically finds the correct method names
|
||||
*/
|
||||
|
||||
console.log("\n[*] Request Body Capture (Reflection-based)\n");
|
||||
|
||||
Java.perform(function() {
|
||||
|
||||
try {
|
||||
var AuthHeaderInterceptor = Java.use("com.adif.elcanomovil.serviceNetworking.interceptors.AuthHeaderInterceptor");
|
||||
console.log("[+] Found AuthHeaderInterceptor");
|
||||
|
||||
AuthHeaderInterceptor.intercept.implementation = function(chain) {
|
||||
console.log("\n" + "=".repeat(80));
|
||||
console.log("[HTTP REQUEST]");
|
||||
|
||||
try {
|
||||
// Cast chain
|
||||
var ChainClass = Java.use("j3.g");
|
||||
var chainObj = Java.cast(chain, ChainClass);
|
||||
|
||||
// Get request
|
||||
var requestField = chainObj.getClass().getDeclaredField("e");
|
||||
requestField.setAccessible(true);
|
||||
var request = requestField.get(chainObj);
|
||||
|
||||
if (request) {
|
||||
// Get URL
|
||||
var urlField = request.getClass().getDeclaredField("a");
|
||||
urlField.setAccessible(true);
|
||||
var urlObj = urlField.get(request);
|
||||
console.log("[URL] " + urlObj.toString());
|
||||
|
||||
// Get method
|
||||
var methodField = request.getClass().getDeclaredField("b");
|
||||
methodField.setAccessible(true);
|
||||
var method = methodField.get(request);
|
||||
console.log("[METHOD] " + method);
|
||||
|
||||
// Get request body
|
||||
var bodyField = request.getClass().getDeclaredField("d");
|
||||
bodyField.setAccessible(true);
|
||||
var reqBody = bodyField.get(request);
|
||||
|
||||
if (reqBody) {
|
||||
try {
|
||||
// Load Buffer class
|
||||
var Buffer = Java.use("r3.f");
|
||||
var buffer = Buffer.$new();
|
||||
|
||||
// Call writeTo with the buffer
|
||||
reqBody.writeTo(buffer);
|
||||
|
||||
// Use reflection to find readUtf8() method
|
||||
var methods = buffer.getClass().getMethods();
|
||||
var readUtf8Method = null;
|
||||
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
var method = methods[i];
|
||||
var methodName = method.getName();
|
||||
var returnType = method.getReturnType().getName();
|
||||
var paramCount = method.getParameterTypes().length;
|
||||
|
||||
// Look for a method that returns String and has no parameters
|
||||
if (returnType === "java.lang.String" && paramCount === 0) {
|
||||
// This is likely readUtf8()
|
||||
readUtf8Method = method;
|
||||
console.log("[DEBUG] Found string method: " + methodName + "()");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (readUtf8Method) {
|
||||
readUtf8Method.setAccessible(true);
|
||||
var bodyContent = readUtf8Method.invoke(buffer);
|
||||
|
||||
console.log("\n[REQUEST BODY]");
|
||||
if (bodyContent && bodyContent.length > 0) {
|
||||
if (bodyContent.length > 3000) {
|
||||
console.log(bodyContent.substring(0, 3000));
|
||||
console.log("\n... (truncated, total: " + bodyContent.length + " chars)");
|
||||
} else {
|
||||
console.log(bodyContent);
|
||||
}
|
||||
} else {
|
||||
console.log("(empty)");
|
||||
}
|
||||
} else {
|
||||
console.log("[REQUEST BODY] Could not find readUtf8() method");
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log("[REQUEST BODY ERROR] " + e);
|
||||
console.log("[STACK] " + e.stack);
|
||||
}
|
||||
} else {
|
||||
console.log("[REQUEST BODY] null");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log("[ERROR] " + e);
|
||||
console.log("[STACK] " + e.stack);
|
||||
}
|
||||
|
||||
console.log("=".repeat(80) + "\n");
|
||||
|
||||
// Call original
|
||||
return this.intercept(chain);
|
||||
};
|
||||
|
||||
console.log("[*] Hook installed!\n");
|
||||
|
||||
} catch (e) {
|
||||
console.log("[-] Failed: " + e);
|
||||
}
|
||||
});
|
||||
203
test_corrected_api.py
Normal file
203
test_corrected_api.py
Normal file
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script para probar los endpoints con los valores correctos
|
||||
obtenidos del código decompilado
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
# Headers correctos
|
||||
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):
|
||||
"""Probar un endpoint y mostrar resultado"""
|
||||
print(f"\n{'='*70}")
|
||||
print(f"TEST: {name}")
|
||||
print(f"{'='*70}")
|
||||
print(f"URL: {url}")
|
||||
|
||||
if data:
|
||||
print(f"Body:\n{json.dumps(data, indent=2)}")
|
||||
|
||||
try:
|
||||
if method == "GET":
|
||||
response = requests.get(url, headers=headers, timeout=10)
|
||||
elif method == "POST":
|
||||
response = requests.post(url, headers=headers, json=data, timeout=10)
|
||||
else:
|
||||
print(f"❌ Método {method} no soportado")
|
||||
return False
|
||||
|
||||
print(f"\nStatus: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ SUCCESS")
|
||||
result = response.json()
|
||||
print(f"\nResponse Preview (primeros 500 chars):")
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False)[:500])
|
||||
if len(json.dumps(result)) > 500:
|
||||
print("...")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ FAILED")
|
||||
print(f"Response: {response.text[:300]}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ EXCEPTION: {str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 70)
|
||||
print("PRUEBAS CON VALORES CORRECTOS DEL CÓDIGO DECOMPILADO")
|
||||
print("=" * 70)
|
||||
|
||||
results = {}
|
||||
|
||||
# Test 1: Salidas con State correcto (BOTH en lugar de ALL)
|
||||
print("\n\n### TEST 1: Departures con State=BOTH ###")
|
||||
results['departures_both'] = test_endpoint(
|
||||
"Salidas - Madrid Atocha (State=BOTH, TrafficType=ALL)",
|
||||
"POST",
|
||||
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/departures/traffictype/",
|
||||
HEADERS_CIRCULATION,
|
||||
{
|
||||
"commercialService": "BOTH", # Correcto: BOTH (no ALL)
|
||||
"commercialStopType": "BOTH", # Correcto: BOTH (no ALL)
|
||||
"destinationStationCode": None,
|
||||
"originStationCode": None,
|
||||
"page": {
|
||||
"pageNumber": 0 # Correcto: pageNumber (no page+size)
|
||||
},
|
||||
"stationCode": "10200", # Madrid Atocha
|
||||
"trafficType": "ALL" # Correcto: ALL existe en TrafficType
|
||||
}
|
||||
)
|
||||
|
||||
# Test 2: Salidas con State YES y NOT
|
||||
print("\n\n### TEST 2: Departures con State=YES ###")
|
||||
results['departures_yes'] = test_endpoint(
|
||||
"Salidas - Madrid Atocha (State=YES)",
|
||||
"POST",
|
||||
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/departures/traffictype/",
|
||||
HEADERS_CIRCULATION,
|
||||
{
|
||||
"commercialService": "YES", # Correcto: YES
|
||||
"commercialStopType": "NOT", # Correcto: NOT (no NO)
|
||||
"destinationStationCode": None,
|
||||
"originStationCode": None,
|
||||
"page": {
|
||||
"pageNumber": 0
|
||||
},
|
||||
"stationCode": "10200",
|
||||
"trafficType": "CERCANIAS"
|
||||
}
|
||||
)
|
||||
|
||||
# Test 3: Prueba con TrafficType AVLDMD (correcto)
|
||||
print("\n\n### TEST 3: Departures con TrafficType=AVLDMD ###")
|
||||
results['departures_avldmd'] = test_endpoint(
|
||||
"Salidas - Madrid Atocha (TrafficType=AVLDMD)",
|
||||
"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": "AVLDMD" # Correcto: AVLDMD (no LARGA_DISTANCIA)
|
||||
}
|
||||
)
|
||||
|
||||
# Test 4: Station Observations con stationCodes (array)
|
||||
print("\n\n### TEST 4: Station Observations (stationCodes array) ###")
|
||||
results['station_observations'] = test_endpoint(
|
||||
"Observaciones de Estación (array)",
|
||||
"POST",
|
||||
f"{BASE_STATIONS}/portroyalmanager/secure/stationsobservations/",
|
||||
HEADERS_STATIONS,
|
||||
{
|
||||
"stationCodes": ["10200", "10302"] # Correcto: stationCodes (array, no stationCode)
|
||||
}
|
||||
)
|
||||
|
||||
# Test 5: OneOrSeveralPaths
|
||||
print("\n\n### TEST 5: OneOrSeveralPaths ###")
|
||||
results['onepaths'] = test_endpoint(
|
||||
"Detalles de Ruta Específica",
|
||||
"POST",
|
||||
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpathdetails/onepaths/",
|
||||
HEADERS_CIRCULATION,
|
||||
{
|
||||
"allControlPoints": True,
|
||||
"commercialNumber": None,
|
||||
"destinationStationCode": "71801", # Barcelona Sants
|
||||
"launchingDate": None,
|
||||
"originStationCode": "10200" # Madrid Atocha
|
||||
}
|
||||
)
|
||||
|
||||
# Test 6: Between Stations
|
||||
print("\n\n### TEST 6: Between Stations ###")
|
||||
results['between_stations'] = test_endpoint(
|
||||
"Entre Estaciones (Madrid - 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"
|
||||
}
|
||||
)
|
||||
|
||||
# Resumen
|
||||
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
|
||||
|
||||
for test_name, result in results.items():
|
||||
status = "✅ PASS" if result else "❌ FAIL"
|
||||
print(f"{status} - {test_name}")
|
||||
|
||||
print(f"\nTotal: {total} | Pasadas: {passed} | Fallidas: {failed}")
|
||||
|
||||
if passed == total:
|
||||
print("\n🎉 ¡Todas las pruebas pasaron! La documentación es correcta.")
|
||||
else:
|
||||
print(f"\n⚠️ {failed} prueba(s) fallaron. Revisar los errores arriba.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
180
test_corrected_api_v2.py
Normal file
180
test_corrected_api_v2.py
Normal file
@@ -0,0 +1,180 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script para probar los endpoints OMITIENDO campos null
|
||||
(en lugar de enviarlos explícitamente como null)
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
# Headers correctos
|
||||
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):
|
||||
"""Probar un endpoint y mostrar resultado"""
|
||||
print(f"\n{'='*70}")
|
||||
print(f"TEST: {name}")
|
||||
print(f"{'='*70}")
|
||||
print(f"URL: {url}")
|
||||
|
||||
if data:
|
||||
print(f"Body:\n{json.dumps(data, indent=2)}")
|
||||
|
||||
try:
|
||||
if method == "GET":
|
||||
response = requests.get(url, headers=headers, timeout=10)
|
||||
elif method == "POST":
|
||||
response = requests.post(url, headers=headers, json=data, timeout=10)
|
||||
else:
|
||||
print(f"❌ Método {method} no soportado")
|
||||
return False
|
||||
|
||||
print(f"\nStatus: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ SUCCESS")
|
||||
result = response.json()
|
||||
print(f"\nResponse Preview (primeros 1000 chars):")
|
||||
resp_str = json.dumps(result, indent=2, ensure_ascii=False)
|
||||
print(resp_str[:1000])
|
||||
if len(resp_str) > 1000:
|
||||
print("...")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ FAILED")
|
||||
print(f"Response: {response.text[:300]}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ EXCEPTION: {str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 70)
|
||||
print("PRUEBAS OMITIENDO CAMPOS NULL")
|
||||
print("=" * 70)
|
||||
|
||||
results = {}
|
||||
|
||||
# Test 1: Salidas - SOLO campos requeridos
|
||||
print("\n\n### TEST 1: Departures - SOLO campos necesarios ###")
|
||||
results['departures_minimal'] = test_endpoint(
|
||||
"Salidas - Madrid Atocha (campos mínimos)",
|
||||
"POST",
|
||||
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/departures/traffictype/",
|
||||
HEADERS_CIRCULATION,
|
||||
{
|
||||
"commercialService": "BOTH",
|
||||
"commercialStopType": "BOTH",
|
||||
"page": {
|
||||
"pageNumber": 0
|
||||
},
|
||||
"stationCode": "10200",
|
||||
"trafficType": "ALL"
|
||||
# Omitiendo destinationStationCode, originStationCode que son null
|
||||
}
|
||||
)
|
||||
|
||||
# Test 2: Station Observations
|
||||
print("\n\n### TEST 2: Station Observations ###")
|
||||
results['station_observations'] = test_endpoint(
|
||||
"Observaciones de Estación",
|
||||
"POST",
|
||||
f"{BASE_STATIONS}/portroyalmanager/secure/stationsobservations/",
|
||||
HEADERS_STATIONS,
|
||||
{
|
||||
"stationCodes": ["10200"]
|
||||
}
|
||||
)
|
||||
|
||||
# Test 3: OneOrSeveralPaths - solo campos necesarios
|
||||
print("\n\n### TEST 3: OneOrSeveralPaths (campos mínimos) ###")
|
||||
results['onepaths_minimal'] = test_endpoint(
|
||||
"Detalles de Ruta - solo estaciones",
|
||||
"POST",
|
||||
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpathdetails/onepaths/",
|
||||
HEADERS_CIRCULATION,
|
||||
{
|
||||
"destinationStationCode": "71801",
|
||||
"originStationCode": "10200"
|
||||
# Omitiendo allControlPoints, commercialNumber, launchingDate
|
||||
}
|
||||
)
|
||||
|
||||
# Test 4: Between Stations
|
||||
print("\n\n### TEST 4: Between Stations (campos mínimos) ###")
|
||||
results['between_stations'] = test_endpoint(
|
||||
"Entre Estaciones (Madrid - Barcelona)",
|
||||
"POST",
|
||||
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/betweenstations/traffictype/",
|
||||
HEADERS_CIRCULATION,
|
||||
{
|
||||
"commercialService": "BOTH",
|
||||
"commercialStopType": "BOTH",
|
||||
"destinationStationCode": "71801",
|
||||
"originStationCode": "10200",
|
||||
"page": {
|
||||
"pageNumber": 0
|
||||
},
|
||||
"trafficType": "ALL"
|
||||
# Omitiendo stationCode que es null
|
||||
}
|
||||
)
|
||||
|
||||
# Test 5: Arrivals
|
||||
print("\n\n### TEST 5: Arrivals ###")
|
||||
results['arrivals'] = test_endpoint(
|
||||
"Llegadas - Madrid Atocha",
|
||||
"POST",
|
||||
f"{BASE_CIRCULATION}/portroyalmanager/secure/circulationpaths/arrivals/traffictype/",
|
||||
HEADERS_CIRCULATION,
|
||||
{
|
||||
"commercialService": "BOTH",
|
||||
"commercialStopType": "BOTH",
|
||||
"page": {
|
||||
"pageNumber": 0
|
||||
},
|
||||
"stationCode": "10200",
|
||||
"trafficType": "ALL"
|
||||
}
|
||||
)
|
||||
|
||||
# Resumen
|
||||
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
|
||||
|
||||
for test_name, result in results.items():
|
||||
status = "✅ PASS" if result else "❌ FAIL"
|
||||
print(f"{status} - {test_name}")
|
||||
|
||||
print(f"\nTotal: {total} | Pasadas: {passed} | Fallidas: {failed}")
|
||||
|
||||
if passed == total:
|
||||
print("\n🎉 ¡Todas las pruebas pasaron!")
|
||||
elif passed > 0:
|
||||
print(f"\n✅ {passed} prueba(s) funcionaron correctamente")
|
||||
else:
|
||||
print(f"\n⚠️ Todas las pruebas fallaron")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user