IPL Vault · Custodia documental · v1.0.0

Una API de custodia documental que no confía en nadie por defecto

Sistemas externos se autentican, suben documentos, consultan su metadata y los descargan de forma protegida. Cada operación queda auditada. Este es el recorrido técnico completo.

Resumen visual

Todo el sistema, en un vistazo

IPL Vault
Comandos y tests7 comandos · 58 tests
AutenticaciónSanctum + abilities
Subida seguraMIME real + hash + UUID
Ciclo de vida6 estados controlados
Antivirusstub, fail-closed
Análisis documentalclasificación, pospuesto
Descarga y metadatasolo estado available
Auditoríatoda acción crítica
Cada rama se detalla en las siguientes diapositivas
Principios

Diez prioridades de seguridad, en este orden

  • No guardar ni servir ficheros desde rutas públicas
  • Proteger todo endpoint funcional con Bearer Token + abilities
  • Login de consumidores en /api/v1/login, tokens temporales
  • Cero acceso cruzado entre consumidores API
  • Extensión, MIME real, tamaño y política siempre validados en backend
  • Nunca devolver rutas internas de storage por API
  • Auditar login, subida, descarga, borrado, tokens y análisis
  • Storage, antivirus, permisos, tokens y análisis encapsulados en servicios
  • Respuestas JSON controladas en todo /api/*
  • Colección Postman donde el login propaga {{token}} solo
Definidas en AGENTS.md
Arquitectura

Stack y separación por capas

Laravel 13 (PHP 8.3+), Sanctum para tokens, MariaDB. API-first: sin backend de gestión con interfaz gráfica salvo la demo técnica.

Controllersreciben la request, delegan, dan forma a la respuesta — cero lógica de negocio
Form Requestsvalidación básica de campos por endpoint
Servicesorquestan: subida, descarga, auth, tokens, auditoría, antivirus, análisis
Policiesautorización por documento: view, download, delete, reanalyze, viewAudit
Models + Jobspersistencia y trabajo asíncrono (escaneo, análisis, purga física)
17 servicios · 3 jobs · 1 policy
Datos

Cuatro modelos, un propietario claro por fichero

ApiConsumer

Autenticable vía Sanctum. uuid, abilities, allowed_ips, is_active, soft deletes. Tiene muchos StoredFile.

StoredFile

uuid público, path interno nunca expuesto, estado del ciclo de vida, scan_status, analysis_status, sha256_hash, soft deletes.

FileAccessLog

Auditoría de solo inserción: acción, IP, user agent, request_id, status_code, metadata. Pertenece a un StoredFile.

ApiConsumerEntityPermission

Permisos granulares por entity_type / entity_id: can_upload, can_read, can_list, can_download, can_delete.

Relaciones 1:N desde ApiConsumer y StoredFile
Autenticación

Tokens temporales, nunca credenciales reutilizables

POST /api/v1/login ApiConsumerAuthService valida is_active Sanctum::createToken(abilities) audita login_ok / login_failed

EnsureApiConsumerIsActive

Rechaza si el consumidor está inactivo aunque el token siga siendo válido.

EnsureIpIsWhitelisted

Exige IP en whitelist si FILE_API_REQUIRE_IP_WHITELIST=true.

ForceJsonResponse + LogApiRequest

Fuerza Accept JSON y añade un request_id de correlación a cada petición.

Expiración configurable vía SANCTUM_TOKEN_EXPIRATION_MINUTES
Ingesta

Subida: seis validaciones antes de tocar disco

StoreFileRequest MimeValidationService UploadPolicyService FilePathBuilder FileNameSanitizer FileHashService (sha256) FileUploadService

MIME real (no solo extensión), coherencia extensión/MIME, tamaño máximo, extensiones bloqueadas siempre (.exe, .php, .sh...), ruta interna por UUID sin caracteres peligrosos. La respuesta nunca incluye el path de storage.

POST /api/v1/files y /api/v1/files/batch (207 en éxito parcial)
Ciclo de vida

Un documento solo se descarga en un estado: available

pending_scan

Esperando antivirus, si está activo.

pending_analysis

Esperando análisis documental, si está activo.

available

Único estado desde el que StoredFilePolicy::download() autoriza.

quarantined

Antivirus detectó infección. Descarga bloqueada.

rejected

No superó la validación de subida.

deleted

Soft delete lógico; purga física vía comando aparte.

Con ambos flags en false, el fichero pasa a available de forma directa
Fase 5 — pospuesta por decisión

Antivirus: el andamiaje existe, el proveedor no

AntivirusScannerService y ScanUploadedFileJob están implementados y testeados, pero sin un proveedor real (ClamAV u otro) conectado en esta fase — Se contempla una segunda versión con la integración de los proveedores.

Con el flag en false (por defecto)

FILE_API_SCAN_UPLOADS=false: el fichero pasa a available directo, sin cola.

Con el flag en true sin proveedor

Fail-closed deliberado: el fichero queda en pending_scan para siempre en vez de exponerse sin escanear.

Fase 8 — pospuesta por decisión

Análisis documental: clasificación, no limpieza

Distinto del antivirus: no busca malware, intenta reconocer el tipo de documento (factura, contrato, nómina...) y marcar si requiere revisión humana. Stub listo, sin proveedor real, FILE_API_ANALYSIS_ENABLED=false por defecto.

POST /api/v1/files/{uuid}/reanalyze — ability files:reanalyze
Consulta

Metadata, descarga, listado — sin revelar lo ajeno

StoredFileResource

Nunca expone path, disk, stored_name, sha256_hash ni api_consumer_id. Solo campos seguros.

Listado por entidad

GET /entities/{type}/{id}/files filtra resultados dentro del controller: sin permiso, responde 200 con lista vacía, no 403 — para no confirmar que existen ficheros ajenos.

EnsureTokenCanAccessEntity (alias api.consumer.entity:{columna}) queda disponible para futuras rutas que sí deban bloquear con 403 en vez de filtrar en silencio.

ApiConsumerEntityPermission gobierna el acceso granular
Trazabilidad

Toda operación crítica queda escrita, sin excepción

Login, logout, subida, descarga, borrado, tokens, escaneo, análisis y reanálisis se auditan en FileAccessLog vía FileAuditService: acción, IP, user agent, request_id, status_code y mensaje.

ConsultaQuién puede verla
GET /files/{uuid}/audit-logsSolo el propietario del documento (policy viewAudit), paginado, orden cronológico inverso
19 acciones auditadas distintas
Operación

Comandos Artisan de gestión

ComandoUso
api-consumer:createCrea consumidor con credenciales y abilities
api-consumer:rotate-token {uuid}Revoca todos los tokens y emite uno nuevo
api-consumer:revoke-token {id}Revoca un token puntual, pide confirmación
files:purge-deleted --days --dry-runPurga física tras el periodo de retención
files:scan-pendingProcesa la cola de antivirus pendiente
files:analyze-pendingProcesa la cola de análisis pendiente
demo:create-consumer --forceCrea o rota el consumidor aislado de la demo técnica
Fase 9
Superficie

Once endpoints, todos bajo /api/v1/

MétodoRutaAbility
POST/loginpúblico
POST/logouttoken válido
POST/filesfiles:upload
POST/files/batchfiles:upload
GET/filesfiles:list
GET/files/{uuid}files:read
GET/files/{uuid}/downloadfiles:download
POST/files/{uuid}/reanalyzefiles:reanalyze
GET/files/{uuid}/audit-logsfiles:audit
DELETE/files/{uuid}files:delete
GET/entities/{type}/{id}/filesfiles:list
Especificación completa en docs/openapi.yaml
Confianza

La suite completa, en verde

58
tests
154
assertions
0
fallos

Feature tests por endpoint (auth, subida, descarga, listado por entidad, análisis, seguridad entre consumidores, demo) y Unit tests de servicios (FilePathBuilder, MimeValidationService, AntivirusScannerService, DocumentAnalysisResultNormalizer). Factories dedicadas para ApiConsumer y StoredFile.

php artisan test
Trazabilidad del proyecto

Catorce fases, orden cronológico real

#FaseEstado
1Instalación y estructura basecompletada
2Modelos y migracionescompletada
3Autenticación de consumidorescompletada
4Subida segura de ficheroscompletada
5Antivirus y gestión de estadospospuesta (stub)
6Descarga protegida y metadatacompletada
7Listado por entidad y auditoríacompletada
8Análisis documental automatizadopospuesta (stub)
9Comandos Artisan de gestióncompletada
10Tests automatizadoscompletada
11Documentación API y Postmancompletada
12Demo técnicacompletada
13Presentación técnica del proyectocompletada
14Índice de documentacióncompletada
Fuente de verdad: docs/fases.md
Referencia

Dónde está todo

Especificación y prueba

docs/openapi.yaml · Swagger UI en /api/documentation · Colección Postman con {{token}} y {{file_uuid}} automáticos

Guías

docs/api.md (uso para terceros) · docs/despliegue.md (servidor remoto) · README.md (instalación y comandos)

Reglas del proyecto

AGENTS.md y docs/technical/{PROJECT_CONTEXT,PIPELINE_RULES,KEY_FILES}.md

Pruébalo tú mismo

Pantalla /demo: login, subida, metadata, descarga y auditoría consumiendo la API real desde el navegador

github.com/reinaldoaf5/ipl-vault
Que falta

El ciclo de documentación, cerrado

La Fase 14 centraliza en README.md los enlaces a toda la documentación ampliada — API, despliegue, esta presentación, Swagger, Postman y docs técnicos — para que cualquiera nuevo en el proyecto sepa dónde mirar sin explorar el repo a mano. Con eso, el ciclo de documentación queda cerrado.