Metadata-Version: 2.4
Name: freedec
Version: 2.0.0
Summary: Servicio seguro de gestión, cifrado y recuperación descentralizada de documentos y contraseñas basado en hash SHA-256 para Django y DRF
License: GPL-3.0-or-later
License-File: LICENSE
Keywords: django,django-rest-framework,security,cryptography,fernet,sha256,zero-knowledge
Author: Jorge
Author-email: jrubioh@pm.me
Requires-Python: >=3.11,<4.0
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 5.1
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Framework :: Django :: 6.1
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Topic :: Security :: Cryptography
Requires-Dist: cryptography (>=43.0,<44.0)
Requires-Dist: django (>=5.1,<7.0)
Requires-Dist: djangorestframework (>=3.15,<4.0)
Project-URL: Repository, https://github.com/jrubioh1/Freedec
Description-Content-Type: text/markdown

# Freedec: Sistema de Entrega y Canje Desatendido de Documentos Confidenciales

[![Python](https://img.shields.io/badge/Python-3.11%20%7C%203.12%20%7C%203.14-blue.svg)](https://python.org)
[![Django](https://img.shields.io/badge/Django-5.1%20%7C%205.2%20%7C%206.0%20%7C%206.1-green.svg)](https://djangoproject.com)
[![DRF](https://img.shields.io/badge/DRF-3.15-red.svg)](https://www.django-rest-framework.org)
[![Version](https://img.shields.io/badge/Version-2.0.0-blue.svg)](https://github.com/jrubioh1/Freedec)
[![Security](https://img.shields.io/badge/Cryptography-SHA--256%20%2B%20OTP%20%2B%20Fernet%20DEK-orange.svg)](https://cryptography.io)
[![Zeroization](https://img.shields.io/badge/Shredding-Zeroization%20O(1)%20RAM-darkgreen.svg)](https://owasp.org)
[![OWASP](https://img.shields.io/badge/OWASP%20Top%2010-Compliant-brightgreen.svg)](https://owasp.org)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)

<p align="center">
  <a href="#seccion-espanol"><b>🇪🇸 Leer en Español (Guía Completa)</b></a> &nbsp;&nbsp;|&nbsp;&nbsp; 
  <a href="#section-english"><b>🇬🇧 Read in English (Full Guide)</b></a>
</p>

---

<a id="seccion-espanol"></a>
# 🇪🇸 Español: Guía de Freedec

Bienvenido a la documentación de **Freedec**, una aplicación Django diseñada para la **entrega y canje desatendido de documentos confidenciales con datos personales**, basada en el cotejo unívoco del Hash SHA-256 del archivo y verificación en tiempo real por OTP (One-Time Password).

---

## 🛡️ Principios de Diseño y Reglas de Oro

1. **CERO TRANSPORTE DEL BINARIO POR EL CLIENTE**:
   El usuario final **NUNCA** sube el archivo `.enc` para identificarse o solicitar acceso. El localizador oficial y prueba de trámite es exclusivamente el **hash SHA-256** del archivo original (cadena hexadecimal de 64 caracteres).
2. **SIN CONTRASEÑAS HUMANAS (Zero Human-Readable Passwords)**:
   Ningún usuario ni administrador visualiza, ingresa o gestiona contraseñas en texto claro. El descifrado es 100% interno y desatendido por el propio código del backend en memoria RAM tras validar el OTP.
3. **BURN-AFTER-READ CON DESTRUCCIÓN SEGURA (Zeroization/Shredding O(1) RAM)**:
   Al primer canje exitoso por un usuario autorizado, el archivo cifrado en disco se sobrescribe con bytes criptográficamente aleatorios en bloques de 64 KB (`os.urandom(CHUNK_SIZE)`), forzando la sincronización en disco físico con `flush()` y `os.fsync()` antes de desvincularlo del sistema de archivos (`os.remove()`), y el registro pasa a estado consumido.

---

## 📑 Índice de Contenidos
1. [Flujo Criptográfico y Operativo](#1-flujo-criptográfico-y-operativo)
2. [Entorno de Pruebas y Sandbox de Staging](#2-entorno-de-pruebas-y-sandbox-de-staging)
3. [Uso de la Interfaz Web (Web GUI)](#3-uso-de-la-interfaz-web-web-gui)
4. [Gestión en Django Admin](#4-gestión-en-django-admin)
5. [Endpoints de la API REST](#5-endpoints-de-la-api-rest)
6. [Comandos de Gestión CLI](#6-comandos-de-gestión-cli)
7. [Configuración de Seguridad en settings.py](#7-configuración-de-seguridad-en-settingspy)

---

## 1. Flujo Criptográfico y Operativo

### Alta del Documento (Admin):
1. El administrador sube el documento original (PDF, LibreOffice u Office) y especifica la lista de correos autorizados.
2. El sistema valida las cabeceras binarias (**Magic Bytes**) y calcula el **hash SHA-256** mediante lectura en bloques de 64 KB (anti-DoS/OOM).
3. Genera una **Clave Maestra de Datos (DEK)** única con `Fernet.generate_key()`.
4. Cifra el contenido del archivo con la DEK y lo guarda en disco como `.enc`.
5. Cifra la DEK internamente con la clave maestra del servidor `settings.FREEDEC_FERNET_KEY` y la almacena en `encrypted_dek`.
6. El administrador entrega el **Hash SHA-256** al usuario como prueba y localizador oficial.

### Solicitud de Acceso:
1. El usuario final accede a `/freedec/solicitar/` e introduce el Hash SHA-256 y su correo electrónico (o mediante enlace con `?hash=...`).
2. Si el hash no existe o el correo no está autorizado, el backend ejecuta un cálculo simulado PBKDF2 para neutralizar ataques de temporización (**Timing Attacks**) y devuelve una respuesta neutra preventiva.
3. Si el documento ya fue consumido (`is_consumed == True`), envía un correo formal al solicitante notificando quién y cuándo lo retiró (`consumed_by`, `consumed_at`), registrando `intento_post_consumo` en la auditoría.
4. Si está activo y autorizado, genera un código **OTP numérico de 6 dígitos** (`secrets.choice`), fija caducidad estricta a 15 minutos, envía el código por correo con enlace a `/freedec/canjear/` y audita `otp_enviado`.

### Canje y Destrucción Física (Burn-After-Read):
1. El usuario introduce el código OTP en `/freedec/canjear/`.
2. Se valida el código mediante comparación en tiempo constante (`secrets.compare_digest`). Si falla 3 veces, el token se bloquea definitivamente (`otp_invalido_bloqueado`).
3. Al validar el OTP, el backend descifra la DEK en RAM usando `FREEDEC_FERNET_KEY`, descifra los bytes del archivo original en memoria, ejecuta la **sobrescritura física con bytes aleatorios (`os.urandom`)** y borrado en disco, marca el documento como consumido y devuelve inmediatamente un `FileResponse` con el archivo original en memoria.

---

## 2. Entorno de Pruebas y Sandbox de Staging (100% Desatendido)

Freedec incluye un comando de arranque integral para montar un entorno de pruebas completo en segundos sin intervención manual:

### Inicialización Rápida
```bash
# 1. Clonar e instalar dependencias con Poetry
git clone https://github.com/jrubioh1/Freedec.git
cd Freedec
poetry install

# 2. Inicializar el sandbox de staging de forma 100% desatendida
poetry run python manage.py setup_staging --clean
```

El comando `setup_staging` realiza automáticamente:
- Genera el archivo de configuración `freedec.ini` desde `freedec.ini.template` si no existe.
- Aplica todas las migraciones de base de datos desde cero.
- Crea el **Administrador Maestro** (`admin` / `admin123`).
- Importa la **malla de Unidades Organizativas (UO)** (`DIRECCION`, `RRHH`, `LEGAL`, `AUDITORIA`) y usuarios departamentales con contraseñas predefinidas.
- Genera y cifra documentos confidenciales de prueba con distintas políticas (`FIRST_ACCESS` y `ALL_RECIPIENTS`).
- Imprime un panel resumen en consola con todas las URLs de acceso, credenciales, hashes SHA-256 y enlaces de 1-clic directos.

### Iniciar el Servidor de Pruebas
```bash
poetry run python manage.py runserver 8000
```

### Ejecutar Suite de Pruebas Automatizadas (Pytest)
```bash
poetry run pytest -v
```

### Demostración Interactiva del Flujo
```bash
poetry run python scripts/demo_flow.py
```

---

## 3. Uso de la Interfaz Web (Web GUI)

* **Solicitar Acceso (`/freedec/solicitar/`)**:
  * Formulario limpio con dos campos: **Hash SHA-256** y **Correo Electrónico**.
  * Soporta parámetro URL directo: `/freedec/solicitar/?hash=<SHA256>`.
  * Redirige a la vista de canje guardando los datos en sesión.
* **Canjear OTP (`/freedec/canjear/`)**:
  * Entrada del código OTP numérico de 6 dígitos.
  * Entrega inmediata del archivo original vía descarga de navegador (`FileResponse`).
  * Destrucción simultánea del archivo cifrado en disco.

---

## 4. Gestión en Django Admin

* **Panel `EncryptedDocumentAdmin`**:
  * Columnas: `original_filename`, `file_hash`, `burn_policy`, `consumption_status_badge`, `expiry_status_badge`, `audit_download_button`, `corporate_email_button`, `terminate_action_button`.
  * **Cero contraseñas**: Ningún campo muestra ni almacena contraseñas humanas.
  * **Descripción editable del documento**: Campo editable con valor corporativo por defecto, modificable tanto en el alta como en la edición y durante procesos de reactivación. Esta descripción se incorpora automáticamente en todas las notificaciones por correo electrónico (OTP, avisos de retirada y advertencias).
  * **Caducidad Automática (Por Defecto 2 Semanas)**: Todo asiento cuenta con una fecha de caducidad (`expires_at`), inicializada por defecto a 14 días desde su registro. La columna `expiry_status_badge` muestra de un vistazo el tiempo restante (`⏳ 13d 22h restantes`), alertas de expiración próxima (`⚠️ 36h restantes`) o estado expirado (`⏱️ Caducado`).
  * **Plantilla de Correo Corporativo (📋 / 📨)**:
    * Botón `📋 Copiar texto correo` en el listado para copiar instantáneamente al portapapeles el texto oficial corporativo con el nombre del archivo, su descripción, el Hash SHA-256 y el enlace directo de acceso.
    * Panel en el detalle del documento con visor del texto preformateado, botón de copia y enlace directo para abrir el cliente de correo (`mailto:`).
  * **Selector de Política de Destrucción (`burn_policy`)**:
    * `⚡ 1er Acceso` (*FIRST_ACCESS*): Trituración y borrado físico inmediato tras el primer canje exitoso.
    * `👥 Todos` (*ALL_RECIPIENTS*): Cada destinatario autorizado dispone de una descarga individual; el archivo se preserva en disco hasta que todos los destinatarios hayan canjeado su copia.
  * **Verificación Dinámica y Reactivación**: Cálculo de SHA-256 en cliente con WebCrypto API; detecta si el documento ya existe y si hay destinatarios previos con canje pendiente, ofreciendo el botón `➕ Volver a añadir pendientes a la lista de correos` para reactivarlo sin colisiones y permitiendo actualizar la descripción.
  * **Inmutabilidad y No Repudio Legal (Prohibición de Borrado)**: Los asientos (`EncryptedDocument`) y las pistas de auditoría (`DocumentAccessLog`) son estrictamente inmutables. El borrado está deshabilitado a nivel de interfaz (`has_delete_permission = False`, eliminación de la acción en lote `delete_selected`) y bloqueado a nivel de modelo ORM (`delete()` arroja `PermissionDenied`).
  * **Finalización de Asiento y Trituración Segura (⏹️)**: En lugar de borrar asientos, el personal autorizado dispone del botón `⏹️ Finalizar` que redirige a una pantalla de confirmación exigiendo un motivo obligatorio. Al confirmar, el sistema tritura físicamente el archivo en disco (`shred_and_delete_file`), marca el asiento como consumido (`is_consumed=True`) y asienta la vicisitud `finalizacion_manual` en el log de auditoría con el motivo y usuario responsable.
  * **Descarga de Auditoría Administrativa**: Botón y acción que permite al personal staff con permiso `can_audit_download` descargar una copia descifrada para fines legales o de auditoría mientras el documento **no haya sido consumido**, **sin destruir el archivo ni marcarlo como consumido**, registrando `admin_descarga_preservada`.
  * **Control de Acceso y Permisos Estrictos de Django**:
    * Toda la autorización de acciones recae estrictamente en el sistema de permisos de Django (`django.contrib.auth.models.Permission`).
    * La condición de `is_staff` solo autoriza el acceso al panel `/admin/`; no confiere privilegios implícitos ni bypasses sobre los modelos.
    * El botón de finalización (`terminate_action_button`) y el botón de descarga administrativa (`audit_download_button`) se filtran dinámicamente según los permisos del usuario (`freedec.change_encrypteddocument`, `freedec.can_audit_download`).
    * Inmutabilidad estricta de auditoría: los registros de acceso (`DocumentAccessLog`) no admiten creación, modificación ni eliminación desde el admin (`has_add_permission=False`, `has_change_permission=False`, `has_delete_permission=False`).
  * **Malla de Unidades Organizativas (UO Emisora y Receptora)**:
    * Los documentos se asocian a una `UO Emisora` y opcionalmente a una `UO Receptora (Malla)`.
    * Aislamiento estricto de datos: usuarios no-superusuarios solo pueden listar y acceder a los documentos vinculados a sus UOs activas.
    * Atajos inteligentes: botones para autocompletar la lista de destinatarios autorizados con el buzón corporativo de la UO o todos sus miembros activos.
  * Inline de auditoría legal de solo lectura (`DocumentAccessLogInline`).

* **Panel `OrganizationalUnitAdmin` e Importación Masiva**:
  * Gestión de Unidades Organizativas con código numérico único, alias, correo de buzón y miembros asignados en línea.
  * Módulo interactivo de **Carga Masiva JSON** (`/admin/freedec/organizationalunit/import-batch/`) reservado para el Rol Maestro (`is_superuser`) para aprovisionar simultáneamente UOs y usuarios con feedback en tiempo real.

---

## 5. Endpoints de la API REST

### 1. Solicitud de Código OTP
```bash
curl -X POST http://127.0.0.1:8000/freedec/api/public/request-access/ \
  -H "Content-Type: application/json" \
  -d '{
    "file_hash": "4c7ded034bb71b3802a0f17dd37226ff26217f37388d1bc50d3a6bf774091497",
    "email": "destinatario@seguro.gob.es"
  }'
```
**Respuesta:**
```json
{
  "status": "processed",
  "message": "Si los datos indicados corresponden a un documento activo y una dirección de correo autorizada, recibirá en breves momentos un código de verificación (OTP) en su buzón."
}
```

### 2. Canje y Descarga con OTP
```bash
curl -X POST http://127.0.0.1:8000/freedec/api/public/consume/ \
  -H "Content-Type: application/json" \
  -d '{
    "file_hash": "4c7ded034bb71b3802a0f17dd37226ff26217f37388d1bc50d3a6bf774091497",
    "email": "destinatario@seguro.gob.es",
    "otp_code": "455653"
  }' \
  --output documento_descifrado.pdf
```

---

## 6. Comandos de Gestión CLI

```bash
# Inicializar entorno staging desatendido
poetry run python manage.py setup_staging --clean

# Carga masiva desatendida de UOs y Usuarios desde archivo JSON
poetry run python manage.py load_batch examples/batch_import_example.json

# Cierre y trituración de asientos caducados (Batch periódico para asientos no canjeados)
poetry run python manage.py close_expired_documents --dry-run
poetry run python manage.py close_expired_documents

# Purga y optimización de base de datos (elimina tokens OTP caducados o usados)
poetry run python manage.py purge_expired_tokens --days=7 --dry-run
poetry run python manage.py purge_expired_tokens

# Listar todos los documentos registrados o finalizar asientos administrativamente
poetry run python manage.py delete_document --list
poetry run python manage.py delete_document <hash_o_archivo>

# Descifrado por OTP mediante CLI (consumo y destrucción Burn-After-Read)
poetry run python manage.py decrypt_document <hash_o_archivo> --otp 123456 --email usuario@empresa.com

# Descarga de auditoría administrativa preservada (Audit Bypass)
poetry run python manage.py decrypt_document <hash_o_archivo> --admin --output copia_auditoria.pdf
```

---

## 7. Formato JSON para Cargas Masivas de UOs y Usuarios

Freedec permite aprovisionar Unidades Organizativas (UO) y Usuarios de forma simultánea tanto desde el panel de administración (`/admin/freedec/organizationalunit/import-batch/`) como por línea de comandos con `load_batch`.

El archivo JSON debe seguir la estructura documentada en [`examples/batch_import_example.json`](examples/batch_import_example.json):

```json
{
  "organizational_units": [
    {
      "numeric_code": 1001,
      "alias": "DIRECCION",
      "name": "Dirección General y Presidencia",
      "email": "direccion@freedec.local",
      "description": "Unidad de gobierno corporativo.",
      "is_active": true
    },
    {
      "numeric_code": 1002,
      "alias": "RRHH",
      "name": "Recursos Humanos y Relaciones Laborales",
      "email": "rrhh@freedec.local",
      "description": "Gestión de nóminas y personal.",
      "is_active": true
    }
  ],
  "users": [
    {
      "username": "admin_dir",
      "email": "admin_dir@freedec.local",
      "first_name": "Elena",
      "last_name": "Vázquez",
      "password": "PasswordDir#2026",
      "uo_alias": "DIRECCION",
      "role": "ADMIN",
      "is_staff": true
    },
    {
      "username": "gestor_rrhh",
      "email": "gestor_rrhh@freedec.local",
      "first_name": "Carlos",
      "last_name": "Mendoza",
      "password": "PasswordRRHH#2026",
      "uo_alias": "RRHH",
      "role": "OPERATOR",
      "is_staff": true
    }
  ]
}
```

### Campos admitidos:
- **`organizational_units`**:
  - `numeric_code` *(int, obligatorio)*: Código numérico único identificativo (ej. `1001`).
  - `alias` *(str, obligatorio)*: Siglas o identificador en mayúsculas (ej. `RRHH`).
  - `name` *(str, obligatorio)*: Denominación oficial completa.
  - `email` *(str, obligatorio)*: Buzón corporativo oficial de la unidad.
  - `description` *(str, opcional)*: Descripción funcional de la UO.
  - `is_active` *(bool, opcional, por defecto `true`)*.
- **`users`**:
  - `username` *(str, obligatorio)*: Nombre de usuario de acceso.
  - `email` *(str, obligatorio)*: Correo electrónico seguro del usuario.
  - `password` *(str, opcional)*: Contraseña inicial (si se omite, se autogenera una segura).
  - `uo_alias` o `uo_numeric_code` *(obligatorio)*: UO a la que se vincula el usuario.
  - `role` *(str, obligatorio)*: Uno de `ADMIN` (gestión de UO), `OPERATOR` (emisión/recepción) o `AUDITOR` (solo lectura y auditoría).
  - `is_staff` *(bool, opcional, por defecto `true`)*: Acceso al portal administrativo.

---

## 8. Configuración por Archivo INI (`freedec.ini`) y ConfigParser

A partir de la versión **2.0.0**, Freedec centraliza su configuración en un archivo estándar `.ini` gestionado mediante el módulo nativo de Python `configparser`, eliminando la dependencia dispersa de variables de entorno sueltas.

Se proporciona una plantilla completa en [`freedec.ini.template`](freedec.ini.template). Copie este archivo a `freedec.ini` para personalizarlo:

```ini
[django]
secret_key = clave_secreta_django
debug = true
allowed_hosts = *

[security]
# Clave maestra Fernet del servidor (32 bytes url-safe base64) para cifrar las DEK
fernet_key = TU_CLAVE_FERNET_BASE64_URL_SAFE_DE_32_BYTES=
max_file_size = 52428800
timing_pbkdf2_iterations = 20000

[email]
# Backend de correo: estándar corporativo SMTP (por defecto, no consola)
backend = django.core.mail.backends.smtp.EmailBackend
host = localhost
port = 25
use_tls = false
use_ssl = false
host_user = 
host_password = 
default_from_email = no-reply@freedec.local
timeout = 5
async_email = true
email_max_workers = 8

[throttling]
web_request_limit_per_minute = 15
api_access_request_rate = 15/minute
api_access_consume_rate = 30/minute
```

> **Validación en arranque:** `FreedecConfig.ready()` detiene el inicio de Django con `ImproperlyConfigured` si `fernet_key` no es válida o está ausente. En producción, el backend SMTP garantiza la entrega real de las contraseñas temporales OTP.

### 8.1. Jerarquía de Búsqueda Automática del `.ini` (Linux y Windows)

Tanto si Freedec se ejecuta de forma autónoma (`standalone`) como si se usa integrada a través de las funciones de abstracción, el sistema resuelve la ruta del `.ini` mediante [`resolve_freedec_ini_path`](freedec/conf.py) siguiendo este orden de prelación:

1. **Ruta explícita indicada en la función de abstracción**:
   - Vía parámetro `ini_file` en `inject_freedec_settings(globals(), ini_file="...")` o `get_freedec_defaults(ini_file="...")`.
   - Vía variable de entorno `FREEDEC_INI_PATH`.
   - *Nota:* Si la ruta indicada es un directorio, buscará automáticamente `freedec.ini` en su interior.
2. **En el raíz del sistema de archivos, en una carpeta que ponga `freedec`**:
   - **Linux / Unix**: `/freedec/freedec.ini` (y `/etc/freedec/freedec.ini`).
   - **Windows**: `C:\freedec\freedec.ini` y `<SystemDrive>\freedec\freedec.ini`.
   - **Multiplataforma unificada**: `Path(root.anchor) / "freedec" / "freedec.ini"`.
3. **En el directorio raíz del proyecto (`BASE_DIR`)**:
   - `BASE_DIR / "freedec" / "freedec.ini"` (en la carpeta `freedec` del proyecto).
   - `BASE_DIR / "freedec.ini"` (directamente en la raíz del proyecto).
4. **En el directorio actual de trabajo (`cwd`)**:
   - `./freedec/freedec.ini` o `./freedec.ini`.
5. **Plantillas fallback**:
   - `freedec.ini.template` en la raíz del proyecto o integrada en el paquete Freedec.

---

## 9. Acoplamiento e Integración en Proyectos Django Existentes

Freedec está concebido arquitectónicamente como una **Django Reusable App** lista para acoplarse directamente a cualquier proyecto Django preexistente (monolito corporativo o backend modular) sin fricciones ni conflictos con configuraciones preexistentes.

A partir de la versión **2.0.0**, Freedec incluye el módulo [`freedec.conf`](freedec/conf.py) con asistentes nativos diseñados específicamente para resolver la configuración en una sola línea.

### 9.1. Instalación del Paquete
```bash
# Vía Poetry
poetry add git+https://github.com/jrubioh1/Freedec.git

# O vía pip
pip install git+https://github.com/jrubioh1/Freedec.git
```

---

### 9.2. Configuración en `settings.py` del Proyecto Anfitrión

Se ofrecen dos alternativas equivalentes:

#### 🟢 Método A: Asistente Automático de 1 Línea (Recomendado)
Añada al final del archivo `settings.py` de su proyecto Django anfitrión:

```python
from freedec import inject_freedec_settings

# Inyecta y fusiona de forma segura toda la configuración necesaria
inject_freedec_settings(globals())
```

El asistente `inject_freedec_settings(globals())`:
1. **Detección inteligente de configuración**: Resuelve automáticamente `freedec.ini` buscando en el raíz del sistema (`/freedec/freedec.ini` en Linux o `C:\freedec\freedec.ini` en Windows), en la carpeta `freedec` del proyecto (`BASE_DIR / "freedec"`), en la raíz del proyecto (`BASE_DIR / "freedec.ini"`), o la ruta/carpeta personalizada pasada en `ini_file`.
2. **`INSTALLED_APPS`**: Registra `rest_framework` y `freedec.apps.FreedecConfig` sin duplicar elementos existentes.
3. **`MIDDLEWARE`**: Detecta y añade `django.middleware.locale.LocaleMiddleware` inmediatamente después de `SessionMiddleware` para habilitar el soporte multi-idioma (Español / Inglés).
4. **`REST_FRAMEWORK` (Mezcla No Destructiva)**: Fusiona las tasas de throttling (`access_request` y `access_consume`) y throttles anónimos en `DEFAULT_THROTTLE_RATES` **sin sobreescribir ni romper** las clases de autenticación (JWT, Token, Session) o permisos configuradas por el anfitrión.
5. **Caché y Almacenamiento**: Garantiza un backend de caché (`LocMemCache`) y rutas de `MEDIA_URL` / `MEDIA_ROOT` seguras si no estaban previamente definidas.
6. **Correo SMTP Asíncrono**: Configura el backend estándar SMTP corporativo y el pool de workers en segundo plano.

> **Sobreescritura dinámica de parámetros:** Puede sobreescribir cualquier clave pasando argumentos con nombre a `inject_freedec_settings`:
> ```python
> inject_freedec_settings(
>     globals(),
>     ini_file="config/entorno_produccion.ini",
>     FREEDEC_MAX_FILE_SIZE=25 * 1024 * 1024,  # Límite a 25 MB
>     FREEDEC_EMAIL_MAX_WORKERS=16,
> )
> ```

---

#### 🛠️ Método B: Configuración Manual y Granular
Si su proyecto utiliza múltiples ficheros de configuración modular (`base.py`, `production.py`) o desea declarar explícitamente cada directiva:

```python
from pathlib import Path

# 1. Aplicaciones instaladas
if "rest_framework" not in INSTALLED_APPS:
    INSTALLED_APPS.append("rest_framework")
if "freedec.apps.FreedecConfig" not in INSTALLED_APPS:
    INSTALLED_APPS.append("freedec.apps.FreedecConfig")

# 2. Middleware multi-idioma
if "django.middleware.locale.LocaleMiddleware" not in MIDDLEWARE:
    MIDDLEWARE.insert(
        MIDDLEWARE.index("django.contrib.sessions.middleware.SessionMiddleware") + 1,
        "django.middleware.locale.LocaleMiddleware"
    )

# 3. Seguridad criptográfica de Freedec
FREEDEC_FERNET_KEY = "TU_CLAVE_FERNET_BASE64_URL_SAFE_DE_32_BYTES="
FREEDEC_MAX_FILE_SIZE = 50 * 1024 * 1024  # 50 MB
FREEDEC_TIMING_PBKDF2_ITERATIONS = 20000

# 4. Correo electrónico corporativo (SMTP Estándar)
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.tu-entorno.es"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = "notificaciones@tu-entorno.es"
EMAIL_TIMEOUT = 5
FREEDEC_ASYNC_EMAIL = True
FREEDEC_EMAIL_MAX_WORKERS = 8

# 5. Throttling anti-ataques de fuerza bruta
REST_FRAMEWORK = {
    **REST_FRAMEWORK,  # Preserva autenticaciones previas
    "DEFAULT_THROTTLE_CLASSES": [
        "rest_framework.throttling.AnonRateThrottle",
    ],
    "DEFAULT_THROTTLE_RATES": {
        **REST_FRAMEWORK.get("DEFAULT_THROTTLE_RATES", {}),
        "anon": "100/minute",
        "access_request": "15/minute",
        "access_consume": "30/minute",
    },
}
FREEDEC_WEB_REQUEST_LIMIT_PER_MINUTE = 15

# 6. Almacenamiento y Caché
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"
```

---

### 9.3. Enrutamiento en `urls.py` del Proyecto Anfitrión

Incorpore el espacio de nombres de Freedec en el `urls.py` principal de su proyecto:

```python
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("admin/", admin.site.urls),
    path("freedec/", include("freedec.urls")),  # Rutas Web GUI y API REST
]
```

Rutas expuestas automáticamente:
- **Web GUI**:
  - `/freedec/solicitar/`: Formulario de solicitud de clave OTP por Hash SHA-256 y correo.
  - `/freedec/canjear/`: Canje de OTP de 6 dígitos y descarga en streaming.
- **API REST**:
  - `/freedec/api/public/request-access/` (`POST` con `file_hash` y `email`).
  - `/freedec/api/public/consume/` (`POST` con `file_hash`, `email` y `otp_code`).
- **Django Admin**:
  - `/admin/freedec/encrypteddocument/`: Panel corporativo de documentos y auditoría.
  - `/admin/freedec/organizationalunit/`: Malla multi-tenant de UOs y panel de importación JSON.

---

### 9.4. Migraciones y Despliegue en el Proyecto Anfitrión

Ejecute las migraciones de Freedec en su base de datos anfitriona:

```bash
# Aplica la migración consolidada única 0001_initial
python manage.py migrate

# Opcional: Aprovisionar UOs y usuarios iniciales desde JSON
python manage.py load_batch ruta/a/tu_plantilla_uos.json
```

---
---

<a id="section-english"></a>
# 🇬🇧 English: Freedec Guide

Welcome to **Freedec**, a security-focused Django application for **unattended delivery and redemption of confidential documents with personal data**, based on strict SHA-256 file hash matching and real-time One-Time Password (OTP) verification.

---

## 🛡️ Design Principles & Golden Rules

1. **ZERO CLIENT BINARY TRANSPORT**: The client NEVER uploads the `.enc` file to identify themselves. The official locator is exclusively the SHA-256 hash (64 hex characters).
2. **ZERO HUMAN-READABLE PASSWORDS**: No plaintext passwords managed by users or admins. Decryption is 100% internal and unattended in RAM after validating the OTP.
3. **BURN-AFTER-READ WITH SECURE DESTRUCTION (Zeroization/Shredding)**: Upon the first successful redemption by an authorized user, the encrypted file on disk is overwritten with cryptographically random bytes (`os.urandom`), flushed and synced to physical storage (`flush()` / `os.fsync()`), and deleted from the filesystem (`os.remove()`).

---

## 📑 Table of Contents
1. [Cryptographic Lifecycle](#1-cryptographic-lifecycle)
2. [Testing & Sandbox Setup](#2-testing--sandbox-setup)
3. [Web GUI Usage](#3-web-gui-usage)
4. [Django Admin Management](#4-django-admin-management)
5. [REST API Endpoints](#5-rest-api-endpoints)
6. [CLI Management Commands](#6-cli-management-commands)
7. [Security Configuration in settings.py](#7-security-configuration-in-settingspy)

---

## 1. Cryptographic Lifecycle

* **Upload (Admin)**: File validated by Magic Bytes, unique DEK generated with `Fernet.generate_key()`, encrypted in storage as `.enc`, DEK encrypted with `FREEDEC_FERNET_KEY`. SHA-256 provided to recipient as sole locator.
* **Access Request**: User submits SHA-256 and email at `/freedec/solicitar/`. If unauthorized or non-existent, simulated PBKDF2 runs to prevent timing attacks. If consumed, email notice is dispatched and `intento_post_consumo` logged. If active and authorized, 6-digit cryptographic OTP is sent with 15-minute strict expiration.
* **Redemption (Burn-After-Read)**: User enters 6-digit OTP at `/freedec/canjear/`. Validated via `secrets.compare_digest` (locked after 3 failed attempts). File decrypted in memory, physical file on disk shredded with `os.urandom`, and delivered as `FileResponse` attachment.

---

## 2. Testing & Sandbox Setup

```bash
git clone https://github.com/jrubioh1/Freedec.git
cd Freedec
poetry install
poetry run python manage.py migrate
poetry run python manage.py setup_staging
poetry run pytest -v
poetry run python scripts/demo_flow.py
```

---

## 3. Web GUI Usage

* **Request Access (`/freedec/solicitar/`)**: SHA-256 hash and email input. Supports `?hash=...` prefilling.
* **Redeem OTP (`/freedec/canjear/`)**: 6-digit OTP input with immediate browser RAM streaming download and disk shredding.

---

## 4. Django Admin Management

* **`EncryptedDocumentAdmin` Dashboard**:
  * Columns: `original_filename`, `file_hash`, `burn_policy`, `consumption_status_badge`, `expiry_status_badge`, `audit_download_button`, `corporate_email_button`, `terminate_action_button`.
  * **Zero Passwords**: No field displays or stores human-readable passwords.
  * **Editable Document Description**: Pre-filled default corporate description, editable upon upload, document modification, and during reactivation. Embedded automatically into all email notifications (OTP verification, redemption notices, post-consumption warnings).
  * **Automatic Expiration (Default 2 Weeks)**: Every entry includes an expiration date (`expires_at`), initialized by default to 14 days from creation. The `expiry_status_badge` column provides an at-a-glance badge of time remaining (`⏳ 13d 22h remaining`), warning alerts for pending expiration (`⚠️ 36h remaining`), or expired status (`⏱️ Caducado`).
  * **Corporate Email Template (📋 / 📨)**:
    * Changelist action button (`📋 Copiar texto correo`) for one-click clipboard copying of the official notification text with filename, description, SHA-256 hash, and direct access link.
    * Document detail panel featuring a preformatted template viewer, copy button, and direct `mailto:` link.
  * **Destruction Policy Selector (`burn_policy`)**:
    * `⚡ 1st Access` (*FIRST_ACCESS*): Immediate physical shredding and removal upon the first successful redemption.
    * `👥 All Recipients` (*ALL_RECIPIENTS*): Each authorized recipient receives an individual download; the file remains on disk until all recipients have redeemed their copy.
  * **Dynamic Client-Side Verification & Reactivation**: Computes SHA-256 in the browser via WebCrypto API; detects if the document already exists and if prior recipients have pending access, providing a `➕ Add pending to recipient list` button to reactivate without hash collisions while allowing description updates.
  * **Strict Immutability & Legal Non-Repudiation (Deletion Disabled)**: Entries (`EncryptedDocument`) and audit logs (`DocumentAccessLog`) are strictly immutable. Deletion is disabled in Django Admin (`has_delete_permission = False`, removal of batch `delete_selected`) and forbidden at the ORM model level (`delete()` raises `PermissionDenied`).
  * **Entry Termination & Secure Physical Shredding (⏹️)**: Authorized staff can terminate active entries via the `⏹️ Finalizar` button, which opens a confirmation page requiring a mandatory justification reason. Confirmation physically shreds the file (`shred_and_delete_file`), marks the document as consumed (`is_consumed=True`), and registers a `finalizacion_manual` vicisitud in the audit log preserving all historical evidence.
  * **Administrative Audit Inspection**: Action and button allowing staff users with explicit `freedec.can_audit_download` permission to download a decrypted copy for legal compliance while the document **has not been consumed**, **without destroying the file or marking it as consumed**, logging `admin_descarga_preservada`.
  * **Strict Django Model Permissions**:
    * All authorization actions strictly rely on Django's model permission system (`django.contrib.auth.models.Permission`).
    * Being `is_staff` only grants access to `/admin/`; it never bypasses model-level authorization.
    * Action buttons (`terminate_action_button`, `audit_download_button`) are conditionally displayed based on active user permissions (`freedec.change_encrypteddocument`, `freedec.can_audit_download`).
    * Audit log immutability: `DocumentAccessLog` records cannot be added, altered, or deleted manually from Django Admin (`has_add_permission=False`, `has_change_permission=False`, `has_delete_permission=False`).
  * **Organizational Unit Mesh (Origin & Target UO)**:
    * Documents are linked to an `Origin UO` and optionally a `Target UO (Mesh)`.
    * Strict multi-tenant isolation: non-superuser staff members can strictly view and operate documents associated with their active UO memberships.
    * Interactive helpers: buttons to auto-populate authorized recipient lists with the target UO's corporate mailbox or all active departmental members.
  * Read-only legal access logs inline (`DocumentAccessLogInline`).

* **`OrganizationalUnitAdmin` & Bulk Provisioning**:
  * Manage flat Organizational Units with unique numeric code, alias, corporate email, and member inlines.
  * Interactive **JSON Bulk Import** dashboard (`/admin/freedec/organizationalunit/import-batch/`) reserved for the Master Role (`is_superuser`) for simultaneous, idempotent provisioning of UOs and users.

---

## 5. REST API Endpoints

* `POST /freedec/api/public/request-access/` (`file_hash`, `email`)
* `POST /freedec/api/public/consume/` (`file_hash`, `email`, `otp_code`)

---

## 6. CLI Management Commands

* `poetry run python manage.py load_batch config_provisioning.json`
* `poetry run python manage.py close_expired_documents --dry-run`
* `poetry run python manage.py close_expired_documents`
* `poetry run python manage.py purge_expired_tokens --days=7 --dry-run`
* `poetry run python manage.py purge_expired_tokens`
* `poetry run python manage.py delete_document --list`
* `poetry run python manage.py delete_document <hash_or_filename>`
* `poetry run python manage.py decrypt_document <hash> --otp 123456 --email user@corp.com`
* `poetry run python manage.py decrypt_document <hash> --admin --output audit.pdf`

---

## 7. Security Configuration in settings.py

```python
# Mandatory Fernet master key (32 bytes base64 url-safe)
# Generated via cryptography.fernet.Fernet.generate_key().decode()
FREEDEC_FERNET_KEY = "YOUR_FERNET_KEY_BASE64_URL_SAFE_32_BYTES="

# Maximum allowed file upload size (default 50 MB)
FREEDEC_MAX_FILE_SIZE = 50 * 1024 * 1024
```

> **Startup validation:** `FreedecConfig.ready()` halts Django startup with `ImproperlyConfigured` if `FREEDEC_FERNET_KEY` is missing or invalid.

---

## 8. Integration into Host Django Projects

Freedec is designed as a modular, pluggable **Django Reusable App**. It can be integrated into any existing Django codebase with minimal effort.

Starting with **v2.0.0**, Freedec includes [`freedec.conf`](freedec/conf.py) providing helper functions to inject settings into any host project automatically.

### Method A: One-Line Automatic Injection (Recommended)
Add to the end of your host `settings.py`:

```python
from freedec import inject_freedec_settings

# Automatically merges apps, middleware, throttles, SMTP, caches, and storage
inject_freedec_settings(globals())
```

What `inject_freedec_settings(globals())` does:
- Discovers and loads `freedec.ini` by hierarchical search: filesystem root (`/freedec/freedec.ini` on Linux, `C:\freedec\freedec.ini` on Windows), project `freedec/` subfolder, project root, or custom path passed to `ini_file`.
- Appends `rest_framework` and `freedec.apps.FreedecConfig` to `INSTALLED_APPS` without duplicates.
- Inserts `django.middleware.locale.LocaleMiddleware` immediately after `SessionMiddleware`.
- Merges throttling rates (`access_request`, `access_consume`) into `REST_FRAMEWORK` without altering existing authentication or permission classes.
- Ensures a valid `CACHES` backend (`LocMemCache`) and `MEDIA_ROOT` / `MEDIA_URL` paths.
- Configures corporate SMTP email settings and asynchronous worker pools.

### Method B: Manual Granular Configuration
For partitioned settings architectures (`base.py`, `production.py`):

```python
INSTALLED_APPS += [
    "rest_framework",
    "freedec.apps.FreedecConfig",
]

MIDDLEWARE += [
    "django.middleware.locale.LocaleMiddleware",
]

FREEDEC_FERNET_KEY = "YOUR_FERNET_KEY_BASE64_URL_SAFE_32_BYTES="
FREEDEC_MAX_FILE_SIZE = 50 * 1024 * 1024
FREEDEC_TIMING_PBKDF2_ITERATIONS = 20000

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.corp.local"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = "no-reply@corp.local"
FREEDEC_ASYNC_EMAIL = True
FREEDEC_EMAIL_MAX_WORKERS = 8

REST_FRAMEWORK = {
    **REST_FRAMEWORK,
    "DEFAULT_THROTTLE_CLASSES": [
        "rest_framework.throttling.AnonRateThrottle",
    ],
    "DEFAULT_THROTTLE_RATES": {
        **REST_FRAMEWORK.get("DEFAULT_THROTTLE_RATES", {}),
        "anon": "100/minute",
        "access_request": "15/minute",
        "access_consume": "30/minute",
    },
}
```

### URL Configuration
Include Freedec URLs in your host `urls.py`:

```python
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("admin/", admin.site.urls),
    path("freedec/", include("freedec.urls")),
]
```

### Apply Migrations
```bash
python manage.py migrate
```



