Saltar a contenido

Almacenamiento de ficheros

Gestión Civis abstrae el almacenamiento de ficheros (adjuntos, PDFs generados, documentos) tras una interfaz común, con dos implementaciones: sistema de archivos local y Google Cloud Storage (GCS). Definidas en app/storage_providers.py.

Selección del proveedor

  • La factoría instancia LocalStorageProvider y lo registra en app.extensions["storage"] y app.storage.
  • La variable STORAGE_PROVIDER (local por defecto, o gcs) determina la estrategia; en local la ruta base se toma de STORAGE_LOCAL_PATH (por defecto ./uploads).
storage = app.extensions["storage"]      # o app.storage
key = storage.save_bytes(pdf, "recibo.pdf", prefix="reg", subdir="registro/recibos")

Interfaz común (BaseStorageProvider)

Método Propósito
init_app(app) Inicializa el proveedor con la app.
upload_file(file, filename_prefix) Sube un fichero de la request.
save_bytes(data, filename, prefix, subdir) Guarda bytes directamente.
register_existing(src_path, ...) Copia un fichero ya existente.
move_file(src_path, ...) Mueve un fichero.
get_download_url(file_path, token) URL de descarga.
serve_file(file_path) Sirve el fichero inline.
get_file_path(file_path) Ruta local validada.

LocalStorageProvider

  • init_app() crea la carpeta uploads/ relativa a la raíz del proyecto.
  • Los nombres se hacen únicos: {prefix}_{uuid8}_{nombre_original}.
  • save_bytes(..., subdir="...") permite organizar por subcarpetas y devuelve una storage key relativa con separadores / (multiplataforma).
  • register_existing() copia (no mueve) con reintentos, para evitar bloqueos de fichero en Windows.

Protección contra path traversal

def _validate_path(self, file_path):
    resolved = os.path.realpath(os.path.join(self.upload_folder, file_path))
    if not resolved.startswith(os.path.realpath(self.upload_folder)):
        raise PermissionError("Acceso denegado: ruta fuera del directorio permitido")
    return resolved

Mitigación verificada

La validación de rutas impide que un file_path malicioso escape de la carpeta uploads/ (remediado en la auditoría de seguridad ENS/ENI).

GCSStorageProvider

  • init_app() configura el cliente con GOOGLE_APPLICATION_CREDENTIALS y GCS_BUCKET_NAME. Si falla, deja el cliente a None (degradación controlada).
  • get_download_url() genera URLs firmadas v4 con caducidad de 15 minutos.

Límite de subida

MAX_CONTENT_LENGTH = 10 MB (en Config). Si se supera, la app responde 413 con un mensaje en español (manejador de RequestEntityTooLarge).

Restricciones adicionales por módulo

Algunos módulos imponen límites más estrictos. Por ejemplo, Registro aplica las restricciones SICRES 3.0: máximo 5 adjuntos, 10 MB por adjunto y 15 MB en total por asiento registral.