# Guía de Seguridad

Esta guía cubre autenticación, autorización y mejores prácticas de seguridad en SLM Framework.

## Autenticación

### Descripción General

SLM Framework utiliza un proceso de autenticación de dos factores:

1. El usuario ingresa su dirección de email
2. El sistema envía un código de seguridad de 6 dígitos al email
3. El usuario ingresa el código para completar el inicio de sesión
4. La sesión se crea y almacena en Elasticsearch

### Flujo de Autenticación

```
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Ingresar  │────▶│   Validar   │────▶│   Generar   │
│    Email    │     │   Usuario   │     │    Token    │
└─────────────┘     └─────────────┘     └──────┬──────┘
                                               │
                                               ▼
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  Completar  │◀────│   Validar   │◀────│   Enviar    │
│    Login    │     │    Token    │     │   Email     │
└─────────────┘     └─────────────┘     └─────────────┘
```

### Proceso de Login

1. **Envío de Email** (función `login()`):
   - Valida formato de email
   - Verifica si el usuario existe y está activo
   - Genera token de 6 dígitos (válido 10 minutos)
   - Envía token por email
   - Almacena token en sesión

2. **Validación de Token** (función `validateToken()`):
   - Valida código de 6 dígitos
   - Verifica que el token no haya expirado
   - Crea sesión autenticada
   - Redirige al dashboard

### Almacenamiento de Sesiones

Las sesiones se almacenan en el índice `sessions` de Elasticsearch:

```json
{
  "timestamp": 1704067200000,
  "human_date": "2024-01-01 00:00:00",
  "session": "{\"user\":{\"id\":\"...\",\"email\":\"...\",\"profile\":{...}}}",
  "user_id": "user123",
  "app": "MIAPP",
  "environment": "dev"
}
```

### Métodos de Sesión

```php
// Verificar si el usuario está autenticado
if (_SLM::auth()) {
    // Usuario está conectado
}

// Obtener datos del usuario actual
$user = _SLM::$session['user'];
$userId = $user['id'];
$email = $user['email'];

// Cerrar sesión
_SLM::logout(); // Redirige a página de login

// Redirección personalizada después de logout
_SLM::logout('./?v=home.main');
```

## Autorización (ACL)

### Listas de Control de Acceso

Los permisos se definen por perfil en formato JSON:

```json
{
  "slm": { "type": "profile" },
  "name": "Administrador",
  "status": "active",
  "acl": {
    "home": {
      "home.main": ["read"]
    },
    "example": {
      "example.items": ["read", "create", "update", "delete"]
    },
    "config": {
      "config.users": ["read", "create", "update", "delete"],
      "config.profile": ["read", "create", "update"]
    }
  }
}
```

### Tipos de Permisos

| Permiso | Descripción |
|---------|-------------|
| `read` | Ver datos |
| `create` | Crear nuevos registros |
| `update` | Modificar registros existentes |
| `delete` | Eliminar registros (eliminación suave) |

### Verificación de Permisos

#### En el Punto de Entrada (Automático)

El punto de entrada (`index.slm`) verifica permisos automáticamente:

```php
$securityModule = _SLM::getSecurityAccess($_vista);
if (count($securityModule) < 1) {
    header("Location: ./"); // Redirigir si no tiene acceso
}
```

#### En Handlers AJAX

```php
function deleteItem(): void
{
    // Obtener permisos para este módulo
    $permissions = _SLM::getSecurityAccess('example.items');

    // Verificar permiso específico
    if (!in_array('delete', $permissions)) {
        exit(json_encode(_response(true, 'Permiso denegado')));
    }

    // Continuar con eliminación...
}
```

#### En Vistas

```php
<?php
$permissions = _SLM::getSecurityAccess('example.items');
$canCreate = in_array('create', $permissions);
$canUpdate = in_array('update', $permissions);
$canDelete = in_array('delete', $permissions);
?>

<?php if ($canCreate): ?>
    <button class="btn btn-primary">Crear Nuevo</button>
<?php endif; ?>

<?php if ($canDelete): ?>
    <button class="btn btn-danger">Eliminar</button>
<?php endif; ?>
```

### Relación Usuario-Perfil

```
Documento Usuario                Documento Perfil
┌─────────────────┐              ┌─────────────────┐
│ id: "user123"   │              │ id: "prof456"   │
│ name: "Juan"    │──referencia─▶│ name: "Admin"   │
│ profile: "..."  │              │ acl: {...}      │
│ status: active  │              │ status: active  │
└─────────────────┘              └─────────────────┘
```

## Mejores Prácticas de Seguridad

### 1. Validación de Entrada

Siempre valida y sanitiza la entrada del usuario:

```php
// Usar funciones helper
validateRequiredFields(['name', 'email']);
$email = validateEmail($_POST['email']);
$name = getPostString('name');
$page = getPostInt('page', 1);
$active = getPostBool('active', false);

// Para consultas Elasticsearch
$searchTerm = escapeEsValue($_POST['search']);
```

### 2. Escape de Salida

Previene XSS escapando la salida:

```php
<!-- En vistas -->
<?= htmlspecialchars($user['name'], ENT_QUOTES, 'UTF-8') ?>

<!-- JavaScript -->
function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}
```

### 3. Manejo de Datos Sensibles

Nunca expongas datos sensibles:

```php
// Excluir contraseña de consultas
$user = getObjectById($id, '_source_excludes=password');

// No registrar contraseñas
$log_data['password'] = '[REDACTADO]';

// No retornar datos sensibles en respuestas
unset($userData['password']);
$response = _response(false, '', ['user' => $userData]);
```

### 4. Verificación de Permisos

Siempre verifica permisos antes de las acciones:

```php
function updateUser(): void
{
    $permissions = _SLM::getSecurityAccess('config.users');

    if (!in_array('update', $permissions)) {
        exit(json_encode(_response(true, 'Acceso denegado')));
    }

    // Continuar con actualización...
}
```

### 5. Limitación de Intentos

El framework incluye limitación de intentos para login:

```php
// Verificar intentos de login (máx 3 en 10 minutos)
checkLoginAttempts($email);

// Verificar intentos de validación de token
checkValidateTokenAttempts($email);

// Registrar intento fallido
registerFailedLoginAttempt($email, $ip);
```

### 6. Headers de Seguridad

Agrega headers de seguridad en la configuración de Apache:

```apache
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
```

### 7. Configuración del Entorno

Mantén datos sensibles en `.env`:

```env
# Nunca subas .env al control de versiones
ES_PASSWORD=tu-contraseña-segura
MAIL_PASSWORD=tu-contraseña-de-correo
```

### 8. Seguridad de Sesiones

```php
// Las sesiones están vinculadas a un token de cookie
_SLM::$appToken = $_COOKIE["slmwafk"];

// Las sesiones expiran cuando expira la cookie
setcookie("slmwafk", $id, time() + (86400 * 30), "/"); // 30 días
```

## Suplantación de Sesión

Los administradores pueden cambiar a la sesión de otro usuario:

```php
// Cambiar a otro usuario
$result = _SLM::switchSession($targetUserId);

if ($result['error']) {
    echo $result['message'];
}

// El ID del admin original se preserva en session['root']
$originalAdmin = _SLM::$session['root'];
```

## Manejo de Contraseñas

Aunque el framework usa autenticación basada en email, si necesitas contraseñas:

```php
// Hashear contraseña
$hashedPassword = password_hash($plainPassword, PASSWORD_BCRYPT);

// Verificar contraseña
if (password_verify($plainPassword, $hashedPassword)) {
    // Contraseña correcta
}

// Nunca uses MD5 para contraseñas
// $hash = md5($password); // NO HAGAS ESTO
```

## Registro de Eventos de Seguridad

Todos los eventos de seguridad se registran:

```php
addToLog('success', 'security', 'login', [
    'email' => $email,
    'ip' => $_SERVER['REMOTE_ADDR']
]);

addToLog('warning', 'security', 'failed_login', [
    'email' => $email,
    'ip' => $_SERVER['REMOTE_ADDR'],
    'reason' => 'Contraseña inválida'
]);
```

## Solución de Problemas

### El Usuario No Puede Iniciar Sesión

1. Verifica que el usuario existe en Elasticsearch
2. Verifica que el status del usuario es "active"
3. Confirma que el usuario tiene un perfil válido asignado
4. Revisa la entrega de email en MailHog

### Permiso Denegado

1. Verifica el ACL del perfil del usuario
2. Verifica que el nombre del módulo coincide con la clave del ACL
3. Verifica el tipo de permiso (read, create, etc.)

### Problemas de Sesión

1. Verifica las cookies del navegador
2. Verifica el índice de sesiones en Elasticsearch
3. Revisa el documento de sesión en Kibana
