=> {
update(state => ({ ...state, isLoading: true }));
-
+
try {
const response = await fetch('/api/v1/auth/login', {
method: 'POST',
@@ -93,7 +93,7 @@ function createAuthStore() {
}
const data: LoginResponse = await response.json();
-
+
// Store auth data
if (typeof window !== 'undefined') {
localStorage.setItem('auth_token', data.access_token);
@@ -117,6 +117,8 @@ function createAuthStore() {
if (typeof window !== 'undefined') {
localStorage.removeItem('auth_token');
localStorage.removeItem('auth_user');
+ // Immediate redirect after cleanup
+ window.location.href = '/login';
}
set(initialState);
},
diff --git a/frontend-internal/src/routes/audit/+page.svelte b/frontend-internal/src/routes/audit/+page.svelte
index a54e242..c47480e 100644
--- a/frontend-internal/src/routes/audit/+page.svelte
+++ b/frontend-internal/src/routes/audit/+page.svelte
@@ -9,9 +9,14 @@
let logs = [];
let stats = null;
let users = [];
+ let incidents = [];
+ let securityAnalysis = null;
let isLoading = false;
+ let isLoadingIncidents = false;
let selectedLog = null;
+ let selectedIncident = null;
let showDetailModal = false;
+ let showIncidentModal = false;
// Paginación
let currentPage = 1;
@@ -19,12 +24,24 @@
let totalLogs = 0;
const perPage = 20;
+ // Paginación de incidentes
+ let incidentsPage = 1;
+ let incidentsTotalPages = 1;
+ let totalIncidents = 0;
+ const incidentsPerPage = 10;
+
// Filtros básicos
let filterUserId = '';
let filterAction = '';
let filterResourceType = '';
let searchText = '';
+ // Filtros de incidentes
+ let filterSeverity = '';
+ let filterIncidentType = '';
+ let filterStatus = '';
+ let incidentSearchText = '';
+
// Filtro multi-tenant (solo para ADMIN/SUPPORT_MANAGER)
let allTenants = false;
@@ -126,6 +143,57 @@
}
}
+ /**
+ * Cargar incidentes de seguridad
+ */
+ async function loadIncidents() {
+ isLoadingIncidents = true;
+ try {
+ const params: any = {
+ page: incidentsPage,
+ per_page: incidentsPerPage
+ };
+
+ // Aplicar filtros de incidentes
+ if (filterSeverity) params.severity = filterSeverity;
+ if (filterIncidentType) params.type = filterIncidentType;
+ if (filterStatus) params.status = filterStatus;
+ if (incidentSearchText) params.search = incidentSearchText;
+
+ // Aplicar filtro multi-tenant si el usuario tiene permiso
+ if (allTenants && canSeeAllTenants) {
+ params.all_tenants = true;
+ }
+
+ const response = await api.get('/audit/security/incidents', params);
+
+ incidents = response.incidents || [];
+ totalIncidents = response.total || 0;
+ incidentsTotalPages = response.total_pages || 1;
+ incidentsPage = response.page || 1;
+ } catch (e) {
+ console.error('Error cargando incidentes:', e);
+ incidents = [];
+ } finally {
+ isLoadingIncidents = false;
+ }
+ }
+
+ /**
+ * Cargar análisis de seguridad
+ */
+ async function loadSecurityAnalysis() {
+ try {
+ const params: any = { hours: 24 };
+ if (allTenants && canSeeAllTenants) {
+ params.all_tenants = true;
+ }
+ securityAnalysis = await api.get('/audit/security/analysis', params);
+ } catch (e) {
+ console.error('Error cargando análisis de seguridad:', e);
+ }
+ }
+
/**
* Cargar logs de auditoría con filtros
*/
@@ -285,16 +353,50 @@
}
/**
- * Obtener color de badge según tipo de acción
+ * Obtener color de badge según tipo de acción (solo escala de grises)
*/
function getActionColor(action: string): string {
- if (action.includes('login')) return 'bg-green-100 text-green-800';
- if (action.includes('logout')) return 'bg-gray-100 text-gray-800';
- if (action.includes('create')) return 'bg-blue-100 text-blue-800';
- if (action.includes('update')) return 'bg-yellow-100 text-yellow-800';
- if (action.includes('delete')) return 'bg-red-100 text-red-800';
- if (action.includes('assign')) return 'bg-purple-100 text-purple-800';
- return 'bg-gray-100 text-gray-800';
+ if (action.includes('delete')) return 'bg-gray-800 text-white';
+ if (action.includes('update')) return 'bg-gray-600 text-white';
+ if (action.includes('login') || action.includes('logout')) return 'bg-gray-400 text-white';
+ if (action.includes('create')) return 'bg-gray-300 text-gray-800';
+ return 'bg-gray-200 text-gray-700';
+ }
+
+ /**
+ * Obtener color de severidad (escala de grises)
+ */
+ function getSeverityColor(severity: string): string {
+ switch(severity?.toLowerCase()) {
+ case 'critical':
+ return 'bg-gray-900 text-white';
+ case 'high':
+ return 'bg-gray-700 text-white';
+ case 'medium':
+ return 'bg-gray-500 text-white';
+ case 'low':
+ return 'bg-gray-300 text-gray-800';
+ default:
+ return 'bg-gray-200 text-gray-700';
+ }
+ }
+
+ /**
+ * Obtener color de estado (escala de grises)
+ */
+ function getStatusColor(status: string): string {
+ switch(status?.toLowerCase()) {
+ case 'active':
+ case 'open':
+ return 'bg-gray-800 text-white';
+ case 'resolved':
+ case 'closed':
+ return 'bg-gray-400 text-white';
+ case 'investigating':
+ return 'bg-gray-600 text-white';
+ default:
+ return 'bg-gray-200 text-gray-700';
+ }
}
/**
@@ -336,6 +438,58 @@
return roleMap[role] || role;
}
+ /**
+ * Ver detalle de un incidente
+ */
+ function viewIncidentDetail(incident: any) {
+ selectedIncident = incident;
+ showIncidentModal = true;
+ }
+
+ /**
+ * Aplicar filtros de incidentes y recargar desde página 1
+ */
+ function applyIncidentFilters() {
+ incidentsPage = 1;
+ loadIncidents();
+ }
+
+ /**
+ * Limpiar filtros de incidentes
+ */
+ function clearIncidentFilters() {
+ filterSeverity = '';
+ filterIncidentType = '';
+ filterStatus = '';
+ incidentSearchText = '';
+ incidentsPage = 1;
+ loadIncidents();
+ }
+
+ /**
+ * Cambiar página de incidentes
+ */
+ function goToIncidentsPage(page: number) {
+ if (page >= 1 && page <= incidentsTotalPages) {
+ incidentsPage = page;
+ loadIncidents();
+ }
+ }
+
+ /**
+ * Formatear fecha simple
+ */
+ function formatSimpleDate(dateString: string): string {
+ const date = new Date(dateString);
+ return date.toLocaleDateString('es-MX', {
+ day: '2-digit',
+ month: '2-digit',
+ year: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+ }
+
/**
* Inicializar datos
*/
@@ -343,6 +497,8 @@
loadStats();
loadUsers();
loadLogs();
+ loadIncidents();
+ loadSecurityAnalysis();
});
@@ -353,7 +509,7 @@
Auditoría del Sistema
Registro de actividades •
-
+
{periodFilter === 'today' ? 'Hoy' :
periodFilter === 'yesterday' ? 'Ayer' :
periodFilter === 'last7days' ? 'Últimos 7 días' :
@@ -369,31 +525,31 @@
@@ -422,7 +578,7 @@
id="custom-date-to"
bind:value={customDateTo}
on:change={applyFilters}
- class="block w-full rounded-md border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
+ class="block w-full rounded-md border-gray-300 shadow-sm focus:border-gray-500 focus:ring-gray-500 sm:text-sm"
/>
@@ -444,7 +600,7 @@
loadLogs();
loadStats();
}}
- class="rounded border-gray-300 text-primary-600 shadow-sm focus:border-primary-500 focus:ring-primary-500 h-4 w-4 mr-3"
+ class="rounded border-gray-300 text-gray-600 shadow-sm focus:border-gray-500 focus:ring-gray-500 h-4 w-4 mr-3"
/>
Ver todos los clientes
@@ -453,7 +609,7 @@
{#if allTenants}
-
+
@@ -473,28 +629,28 @@
Hoy
-
{stats.actions_today}
+
{stats.actions_today}
Esta Semana
-
{stats.actions_this_week}
+
{stats.actions_this_week}
-
-
- {stats.critical_actions_today}
+
+ {stats.critical_actions_today || 0}
-
Acciones críticas hoy
+
Incidentes críticos hoy
{/if}
@@ -519,7 +675,7 @@
Filtros Avanzados
{#if activeFiltersCount > 0}
-
+
{activeFiltersCount}
{/if}
@@ -541,7 +697,7 @@
bind:value={searchText}
on:input={applyFilters}
placeholder="Buscar en acciones..."
- class="block w-full rounded-md border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
+ class="block w-full rounded-md border-gray-300 shadow-sm focus:border-gray-500 focus:ring-gray-500 sm:text-sm"
/>
@@ -552,7 +708,7 @@
id="user"
bind:value={filterUserId}
on:change={applyFilters}
- class="block w-full rounded-md border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
+ class="block w-full rounded-md border-gray-300 shadow-sm focus:border-gray-500 focus:ring-gray-500 sm:text-sm"
>
{#each users as user}
@@ -568,7 +724,7 @@
id="action"
bind:value={filterAction}
on:change={applyFilters}
- class="block w-full rounded-md border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
+ class="block w-full rounded-md border-gray-300 shadow-sm focus:border-gray-500 focus:ring-gray-500 sm:text-sm"
>
{#each Array.from(availableActions).sort() as action}
@@ -584,7 +740,7 @@
id="resource-type"
bind:value={filterResourceType}
on:change={applyFilters}
- class="block w-full rounded-md border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
+ class="block w-full rounded-md border-gray-300 shadow-sm focus:border-gray-500 focus:ring-gray-500 sm:text-sm"
>
{#each Array.from(availableResourceTypes).sort() as resourceType}
@@ -598,7 +754,7 @@
@@ -608,6 +764,130 @@
{/if}
+
+
+
+
+
Incidentes de Seguridad
+ {totalIncidents} incidentes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {#if isLoadingIncidents}
+
+
+
Cargando incidentes...
+
+ {:else if incidents.length === 0}
+
+
+
+
+
No hay incidentes
+
No se encontraron incidentes de seguridad para los filtros seleccionados.
+
+ {:else}
+
+ {#each incidents as incident (incident.id)}
+
viewIncidentDetail(incident)}>
+
+
+
+
+
{incident.title}
+
{incident.description || 'Sin descripción'}
+
+
+
+
+ {incident.severity?.toUpperCase()}
+
+
+ {incident.status?.toUpperCase()}
+
+
+ {formatSimpleDate(incident.created_at)}
+
+
+
+
+ {/each}
+
+
+
+ {#if incidentsTotalPages > 1}
+
+
+ Página {incidentsPage} de {incidentsTotalPages}
+
+
+
+
+
+
+ {/if}
+ {/if}
+
+
+
@@ -672,8 +952,8 @@
{#if log.user_email}
-
-
+
+
{(log.user_name || '?').charAt(0).toUpperCase()}
@@ -701,7 +981,7 @@
@@ -722,8 +1002,8 @@
{#if log.user_email}
-
-
+
+
{(log.user_name || '?').charAt(0).toUpperCase()}
@@ -760,7 +1040,7 @@
+
+{#if showIncidentModal && selectedIncident}
+ showIncidentModal = false}>
+
+
+
+ Información General
+
+
+ - Título:
+ - {selectedIncident.title}
+
+
+ - Severidad:
+ -
+
+ {selectedIncident.severity?.toUpperCase()}
+
+
+
+
+ - Estado:
+ -
+
+ {selectedIncident.status?.toUpperCase()}
+
+
+
+
+ - Fecha:
+ - {formatDate(selectedIncident.created_at)}
+
+ {#if selectedIncident.affected_user}
+
+ - Usuario Afectado:
+ - {selectedIncident.affected_user}
+
+ {/if}
+ {#if selectedIncident.source_ip}
+
+ - IP Origen:
+ - {selectedIncident.source_ip}
+
+ {/if}
+
+
+
+
+ {#if selectedIncident.description}
+
+ Descripción
+
+ {selectedIncident.description}
+
+
+ {/if}
+
+
+ {#if selectedIncident.evidence && selectedIncident.evidence.length > 0}
+
+ Evidencia
+
+
+ {#each selectedIncident.evidence as evidence}
+ - {evidence}
+ {/each}
+
+
+
+ {/if}
+
+
+ {#if selectedIncident.metadata && Object.keys(selectedIncident.metadata).length > 0}
+
+ Información Adicional
+ {JSON.stringify(selectedIncident.metadata, null, 2)}
+
+ {/if}
+
+
+
+ showIncidentModal = false}
+ class="px-4 py-2 bg-white border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50"
+ >
+ Cerrar
+
+
+
+{/if}
+
-{#if showDetailModal && selectedLog}
+{#if showDetailModal && selectedLog}}
showDetailModal = false}>
diff --git a/frontend-internal/src/routes/audit/security/+page.svelte b/frontend-internal/src/routes/audit/security/+page.svelte
index aa3b3e6..0397fe3 100644
--- a/frontend-internal/src/routes/audit/security/+page.svelte
+++ b/frontend-internal/src/routes/audit/security/+page.svelte
@@ -44,28 +44,28 @@
}
/**
- * Obtener color según nivel de riesgo
+ * Obtener color según nivel de riesgo (escala de grises)
*/
function getRiskColor(level: string) {
const colors: any = {
- safe: 'bg-green-100 text-green-800 border-green-300',
- low: 'bg-blue-100 text-blue-800 border-blue-300',
- medium: 'bg-yellow-100 text-yellow-800 border-yellow-300',
- high: 'bg-orange-100 text-orange-800 border-orange-300',
- critical: 'bg-red-100 text-red-800 border-red-300'
+ safe: 'bg-gray-100 text-gray-800 border-gray-300',
+ low: 'bg-gray-200 text-gray-800 border-gray-400',
+ medium: 'bg-gray-400 text-white border-gray-500',
+ high: 'bg-gray-600 text-white border-gray-700',
+ critical: 'bg-gray-900 text-white border-gray-900'
};
return colors[level] || colors.low;
}
/**
- * Obtener color de severidad de amenaza
+ * Obtener color de severidad de amenaza (escala de grises)
*/
function getSeverityColor(severity: string) {
const colors: any = {
- low: 'bg-blue-100 text-blue-800',
- medium: 'bg-yellow-100 text-yellow-800',
- high: 'bg-orange-100 text-orange-800',
- critical: 'bg-red-100 text-red-800'
+ low: 'bg-gray-200 text-gray-800',
+ medium: 'bg-gray-400 text-white',
+ high: 'bg-gray-600 text-white',
+ critical: 'bg-gray-900 text-white'
};
return colors[severity] || colors.low;
}
@@ -179,7 +179,7 @@
-
+
Análisis de Seguridad
@@ -190,7 +190,7 @@
loadSecurityAnalysis()}
- class="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 flex items-center gap-2"
+ class="px-4 py-2 bg-gray-700 text-white rounded-lg hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-gray-500 flex items-center gap-2"
>
@@ -211,19 +211,19 @@
changeAnalysisPeriod(24)}
- class="px-4 py-2 rounded-lg text-sm font-medium transition-colors {analysisHours === 24 ? 'bg-primary-600 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}"
+ class="px-4 py-2 rounded-lg text-sm font-medium transition-colors {analysisHours === 24 ? 'bg-gray-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}"
>
Últimas 24 horas
changeAnalysisPeriod(48)}
- class="px-4 py-2 rounded-lg text-sm font-medium transition-colors {analysisHours === 48 ? 'bg-primary-600 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}"
+ class="px-4 py-2 rounded-lg text-sm font-medium transition-colors {analysisHours === 48 ? 'bg-gray-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}"
>
Últimas 48 horas
changeAnalysisPeriod(168)}
- class="px-4 py-2 rounded-lg text-sm font-medium transition-colors {analysisHours === 168 ? 'bg-primary-600 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}"
+ class="px-4 py-2 rounded-lg text-sm font-medium transition-colors {analysisHours === 168 ? 'bg-gray-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'}"
>
Última semana
@@ -232,7 +232,7 @@
{#if isLoading}
{:else if analysis}
@@ -257,9 +257,9 @@
Amenazas Detectadas
- {analysis.total_threats_detected}
+ {analysis.total_threats_detected}
-
+
@@ -269,9 +269,9 @@
Intentos Fallidos
- {analysis.failed_login_attempts}
+ {analysis.failed_login_attempts}
-
+
@@ -281,9 +281,9 @@
IPs Sospechosas
- {analysis.suspicious_ips_count}
+ {analysis.suspicious_ips_count}
-
+
@@ -293,9 +293,9 @@
Acciones Críticas
- {analysis.critical_actions_count}
+ {analysis.critical_actions_count}
-
+
@@ -304,17 +304,17 @@
{#if analysis.recommended_actions && analysis.recommended_actions.length > 0}
-
+
-
+
- Acciones Recomendadas
+ Acciones Recomendadas
{#each analysis.recommended_actions as action}
- -
-
+
-
+
{action}
@@ -332,12 +332,12 @@
Amenazas Detectadas
{#each analysis.threats as threat}
-
+
-
-
+
@@ -418,7 +418,7 @@
{#if threat.affected_ips.length > 0}
openActionModal(threat, 'block_ip')}
- class="px-3 py-1.5 bg-red-600 text-white text-sm rounded hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 flex items-center gap-1"
+ class="px-3 py-1.5 bg-gray-800 text-white text-sm rounded hover:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-gray-500 flex items-center gap-1"
>
@@ -429,7 +429,7 @@
{#if threat.affected_users.length > 0}
openActionModal(threat, 'force_password_reset')}
- class="px-3 py-1.5 bg-orange-600 text-white text-sm rounded hover:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-orange-500 flex items-center gap-1"
+ class="px-3 py-1.5 bg-gray-600 text-white text-sm rounded hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-500 flex items-center gap-1"
>
@@ -439,7 +439,7 @@
{/if}
openActionModal(threat, 'notify_admin')}
- class="px-3 py-1.5 bg-blue-600 text-white text-sm rounded hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center gap-1"
+ class="px-3 py-1.5 bg-gray-500 text-white text-sm rounded hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500 flex items-center gap-1"
>
@@ -453,12 +453,12 @@
{:else}
-
-
+
+
- Sistema Seguro
- No se detectaron amenazas en el período analizado
+ Sistema Seguro
+ No se detectaron amenazas en el período analizado
{/if}
{/if}
@@ -486,7 +486,7 @@
type="text"
bind:value={actionTarget}
placeholder="IP o email del usuario"
- class="block w-full rounded-md border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
+ class="block w-full rounded-md border-gray-300 shadow-sm focus:border-gray-500 focus:ring-gray-500 sm:text-sm"
/>
@@ -496,7 +496,7 @@
bind:value={actionReason}
rows="3"
placeholder="Razón de la acción de seguridad"
- class="block w-full rounded-md border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
+ class="block w-full rounded-md border-gray-300 shadow-sm focus:border-gray-500 focus:ring-gray-500 sm:text-sm"
>
@@ -508,7 +508,7 @@
bind:value={actionDuration}
min="1"
max="10080"
- class="block w-full rounded-md border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
+ class="block w-full rounded-md border-gray-300 shadow-sm focus:border-gray-500 focus:ring-gray-500 sm:text-sm"
/>
{/if}
@@ -516,13 +516,13 @@
showActionModal = false}
- class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-primary-500"
+ class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500"
>
Cancelar
Ejecutar Acción
|