robo-api
1. Descripción general
Según el pom.xml, el proyecto se describe como "Robo API". Es la API REST que sustenta la gestión de devoluciones de producto (creación, edición, listado, filtrado) para los distintos perfiles de usuario del ecosistema Hawkers: administradores, equipo de atención al cliente (ATC), tiendas físicas y clientes finales. Toda la persistencia se delega en librerías internas (logistics-commons, dynamics-commons); este proyecto no define entidades ni repositorios propios.
2. Información técnica
| Campo | Valor |
|---|---|
artifactId | robo-api |
groupId | com.hawkersco |
version | 1.0.25 |
| Java | 25 |
| Spring Boot | 4.0.6 |
| Tipo de artefacto | jar (ejecutable, API web de larga duración) |
| Módulos | No aplica (proyecto de módulo único) |
3. Arquitectura y diseño
API REST con 12 controladores, seguridad OAuth2/JWT basada en scopes, doble datasource y un modo de pruebas mediante tablas sombra.
.controller—ReturnController,ReturnStoreController,ReturnReasonController,ReturnStatusController,ReturnLineStatusController,ReturnMethodController,ReturnPaymentMethodController,ReturnOriginController,ReturnLogController,ReturnAuth0UserController,OrderController,SourceController,RoboApiController..config—SecurityConfig(JWT + reglas de autorización por scope),CorsConfig(segunda configuración CORS independiente, ver hallazgo en la sección 13),LogisticsDbConfig,DynamicsDbConfig,TestModeFilter,RequestContext,SwaggerConfig,AppConfig,AudienceValidator..job—RoboApiJob(sincronización de usuarios Auth0 desde Google Sheets — ver hallazgo crítico en la sección 13)..util—RoboUtil(extracción de JWT, creación de log de devolución),RoboApiConst,ReturnStatusEnum..model—OrderDynamics,UserAuth0(únicos modelos propios; el resto de entidades viven en las librerías comunes).
flowchart TD
A[Cliente/Frontend] -->|JWT Auth0, scope-based| B[SecurityConfig]
B --> C[Controladores REST]
C -->|X-Test-Mode: true| D[TestModeFilter → RequestContext ThreadLocal]
D -->|redirige a tablas *Test| E[(logistics · Return/ReturnLine/ReturnLog)]
C -->|modo normal| E
C --> F[(dynamics-pro · OrderShippedService)]
G["RoboApiJob (sin @Scheduled activo)"] -.->|Google Sheets → Auth0| H[Auth0 Management API]
Modo de pruebas (TestModeFilter)
Un filtro Servlet lee la cabecera X-Test-Mode; si vale true, activa un flag en RequestContext (ThreadLocal) que los servicios de logistics-commons usan para redirigir las operaciones a tablas sombra (ReturnTest, ReturnLineTest, ReturnLogTest) en lugar de las tablas de producción.
Seguridad
OAuth2 Resource Server con JWT emitido por Auth0 (robo-hawkers.eu.auth0.com), validado contra el JWKS público. Autorización por scope a nivel de ruta en SecurityConfig: SCOPE_admin, SCOPE_atc, SCOPE_store, SCOPE_customer, con reglas granulares por endpoint (p. ej. alta/edición/baja de motivos y métodos de devolución solo para SCOPE_admin).
4. Dependencias principales
| Dependencia | Propósito |
|---|---|
spring-boot-starter-web | Exposición de los endpoints REST |
spring-boot-starter-security + spring-boot-starter-oauth2-resource-server | Autenticación/autorización JWT vía Auth0 |
org.springdoc:springdoc-openapi-starter-webmvc-ui | Documentación Swagger/OpenAPI |
com.hawkersco:logistics-commons | Entidades/servicios de devoluciones, pedidos, usuarios Auth0 de la app |
com.hawkersco:dynamics-commons | OrderShippedService (datos de envío en Dynamics) |
com.hawkersco:sfcc-commons | Utilidades de consulta de catálogo SFCC |
com.hawkersco:slack-client | Notificaciones de error |
com.hawkersco:auth0-client | Cliente para la Management API de Auth0 (usado por RoboApiJob) |
com.hawkersco:pi-function-commons | SheetsServiceUtils (lectura de Google Sheets) |
spring-boot-devtools | Recarga en caliente en desarrollo |
lombok | Generación de código boilerplate |
5. API / Endpoints
Endpoints agrupados por recurso (rutas verificadas en SecurityConfig), todos protegidos por JWT salvo los indicados como públicos:
| Recurso | Rutas | Scopes permitidos |
|---|---|---|
| Público | /swagger-ui/**, /v3/api-docs/**, /check/domain | Ninguno |
| Pedido | /order/info, /order/create-return | admin, atc, store, customer |
| Devolución | /return/info, /return/info-all/** | admin, atc, store (info también customer) |
| Devolución | /return/filter, /return/new, /return/edit/**, /return/delete/** | admin, atc, store |
| Método de devolución | /return-method/info, /search | admin, atc, store (+customer en search) |
| Método de devolución | /return-method/new, /edit/**, /delete/** | admin |
| Método de pago | /return-payment-method/info | admin, atc, store |
| Motivo de devolución | /return-reason/info, /search | admin, atc, store (+customer en info) |
| Motivo de devolución | /return-reason/new, /edit/**, /delete/** | admin |
| Estado de devolución/línea | /return-status/info, /return-line-status/info | admin, atc, store, customer |
| Origen de devolución | /return-origin/info | admin, atc, store |
| Fuente | /source/info | admin, atc, store |
| Tienda de devolución | /return-store/info | admin, atc, store |
| Tienda de devolución | /return-store/edit/** | admin |
| Log de devolución | /return-log/info/** | admin, atc, store |
| Usuario Auth0 | /return-auth0-user/info, /info/**, /edit/** | admin |
Cualquier otra ruta no listada se deniega explícitamente (anyRequest().denyAll()).
6. Integraciones externas
| Sistema | Protocolo | Dirección | Detalle |
|---|---|---|---|
Auth0 (robo-hawkers.eu.auth0.com) | OAuth2/JWT + Management API (Auth0Client) | Entrante/Saliente | Validación de tokens de acceso a la API; gestión de usuarios (alta, asignación de rol) desde RoboApiJob |
| Google Sheets | API de Google (SheetsServiceUtils) | Entrante | Fuente de la lista de usuarios ATC/tienda a sincronizar con Auth0 (job desactivado) |
| Salesforce Commerce Cloud | HTTP (SfccUtils) | Entrante | Consulta de catálogo de producto |
| Slack | HTTP (SlackClient) | Saliente | Notificaciones de error |
PostgreSQL (logistics, dynamics-pro) | JDBC | Entrante/Saliente | Persistencia de devoluciones y datos de envío |
7. Configuración
En producción (application-pro.properties) las credenciales llegan por variables de entorno inyectadas como Secret de Kubernetes; en local (application.properties) el repositorio contiene actualmente valores reales hardcodeados (ver alerta de seguridad).
| Clave | Descripción |
|---|---|
spring.datasource.* | Credenciales de la BD logistics |
dynamics.datasource.* | Credenciales de la BD dynamics-pro |
spring.security.oauth2.resourceserver.jwt.issuer-uri / .audience | Configuración del validador JWT de Auth0 |
auth0.client.url / .id / .secret / .audience | Credenciales de la Management API de Auth0 (usadas por RoboApiJob) |
slack.client.url / .auth.token / .channel.id | Configuración de Slack |
⚠️ Alerta de seguridad (severidad crítica)
Se han encontrado dos problemas de seguridad graves en este proyecto:
- Generación de contraseñas predecibles y registro en texto plano en
RoboApiJob.uploadUsersAuth0(): al crear un nuevo usuario en Auth0, la contraseña asignada esBase64.getEncoder().encodeToString(email.getBytes())— es decir, el propio email del usuario codificado en Base64, una transformación trivialmente reversible y por tanto equivalente a no tener contraseña real. Además, la línealogger.info(returnAuth0User.getEmail() + " - " + password)registra esa contraseña en texto plano en los logs de la aplicación. Aunque los tres métodos deRoboApiJobtienen su@Scheduledcomentado (el job no se ejecuta automáticamente en la configuración actual), el código permanece en el repositorio y podría ejecutarse manualmente o reactivarse sin que nadie repare en esta debilidad. Cualquier cuenta creada por este mecanismo tendría una contraseña calculable por cualquiera que conozca (o adivine) el email del usuario. application.propertieslocal con credenciales reales: contraseñas de ambas bases de datos PostgreSQL,client-secretreal de la aplicación Auth0 Management API, y token de bot de Slack. Ninguno de estos valores se ha reproducido en este documento.
Se recomienda con prioridad máxima:
- Corregir
uploadUsersAuth0()para generar una contraseña aleatoria criptográficamente segura (o, mejor, usar el flujo de invitación/reseteo de contraseña de Auth0 en lugar de asignar una contraseña inicial conocida), y eliminar ellogger.infoque imprime la contraseña. - Si este job ha llegado a ejecutarse alguna vez en producción, forzar el reseteo de contraseña de todos los usuarios Auth0 creados por este mecanismo.
- Rotar el
client-secretde Auth0, las contraseñas de BD y el token de Slack expuestos enapplication.properties.
8. Persistencia
Dos bases de datos PostgreSQL: logistics (entidades de devoluciones, motivos, métodos, tiendas, usuarios Auth0 de la app: Return, ReturnLine, ReturnLog, ReturnAuth0User, ReturnStore, etc., con sus variantes *Test para el modo de pruebas) y dynamics-pro (OrderShippedService). spring.jpa.hibernate.ddl-auto=none en ambas. No hay Flyway/Liquibase en este repositorio.
9. Procesos programados y mensajería
RoboApiJob contiene 3 métodos pensados como tareas programadas (createDbUsersAuth0, updateDbUsersAuth0, uploadUsersAuth0), pero los tres tienen su @Scheduled comentado — no se ejecutan automáticamente pese a que la clase principal habilita @EnableScheduling. Su propósito, si se reactivaran: sincronizar una hoja de Google Sheets con la tabla ReturnAuth0User, asociar usuarios a tiendas, y finalmente crear/actualizar esos usuarios en Auth0 con asignación de rol (ver alerta de seguridad sobre la generación de contraseñas).
10. Ejecución en local
Requisitos previos: JDK 25, Maven, acceso a ambas BD, y credenciales de Auth0 Management API si se necesita ejercitar RoboApiJob.
# Compilar
./mvnw clean install
# Compilar sin tests
./mvnw clean install -DskipTests
# Ejecutar (perfil dev)
./mvnw spring-boot:run
# Ejecutar todos los tests
./mvnw test
# Ejecutar con perfil de producción
java -jar target/robo-api-1.0.25.jar --spring.profiles.active=pro
Documentación interactiva disponible en /swagger-ui/ (ruta pública).
11. Despliegue
- Imagen: construida con
jib-maven-plugin(baseeclipse-temurin:25-jre,containerizingMode=packaged), publicada eneurope-west3-docker.pkg.dev/pi-saldum/pi-repo/robo-api:<tag>. - Orquestación: Kubernetes
Deployment(noCronJob) en el clúster GKEpi-cluster-hw, namespacepi. - CI/CD (Jenkins): pipeline que sustituye
application-pro.propertiesporapplication.propertiesantes de construir.
Job de Jenkins: https://jenkins-pi.hawkersco.net/job/robo-api/
12. Manejo de errores y logging
No se ha localizado un @RestControllerAdvice centralizado en el código revisado. RoboApiJob captura excepciones genéricas por operación (Sheets, Auth0) y registra con Level.SEVERE/WARNING, salvo updateDbUsersAuth0, que relanza IOException como RuntimeException sin capturarla. Logging mediante java.util.logging.Logger estándar (consola).
13. Notas y consideraciones
- Generación de contraseñas predecibles y su registro en logs: ver alerta de seguridad crítica en la sección 7. Es el hallazgo más grave de todo este proyecto.
- Doble configuración de CORS:
SecurityConfigregistra unCorsFilterpropio con precedencia máxima, y por separadoCorsConfigimplementaWebMvcConfigurer.addCorsMappingscon una configuración de orígenes casi idéntica pero no exactamente igual (CorsConfigno incluyeOPTIONSde forma redundante con el filtro, y ambas definen la lista de orígenes por separado, con riesgo de que diverjan). Mismo patrón de duplicación de CORS ya observado engio-api-notifications. CLAUDE.mddescribe correctamente la arquitectura general (dual datasource, modo de pruebas, seguridad por scopes, integraciones), pero no menciona en absoluto el contenido interno deRoboApiJobni el hallazgo de seguridad de la sección 7.- Ver alerta de seguridad en la sección 7 sobre credenciales reales expuestas en
application.properties.