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
LocalStorageProvidery lo registra enapp.extensions["storage"]yapp.storage. - La variable
STORAGE_PROVIDER(localpor defecto, ogcs) determina la estrategia; en local la ruta base se toma deSTORAGE_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 carpetauploads/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 conGOOGLE_APPLICATION_CREDENTIALSyGCS_BUCKET_NAME. Si falla, deja el cliente aNone(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.