Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .claspignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
**/.git/**
**/.gitignore
**/.gitattributes
**/node_modules/**
**/package.json
**/package-lock.json
**/.husky/**
**/.github/**
**/scripts/lint-html-scripts.js
**/*.md
**/.claude/**
**/.gstack/**
**/consolidacion_colab/**
**/.graphifyignore
graphify-out/**
commitlint.config.js
.claspignore
README.md
CONTRIBUTING.md
DESIGN.md
ROADMAP.md
ACTA_CIERRE_SPRINT1.md
ARCHITECTURE_*.md
DOCUMENTACION_TECNICA_VIVA.md
Documento_Tecnico_Aplicacion_Predios.*
TODOS.md
INICIO_SESION.md
CURRENT_STATE.md
PR_SUMMARY.md
PROTOCOLO_CIERRE_SESION_*.md
QA_SPRINT1_UIUX.md
gitleaks-report.json
--help/**
13 changes: 12 additions & 1 deletion .claude/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
{
"permissions": {
"allow": [
"Bash(clasp pull *)",
"Bash(rm -rf _comparacion_produccion)",
"Bash(clasp clone *)",
"Bash(cd \"E:\\\\PROYECTOS\\\\CLAUDE CODE\\\\CREACIÓN APK\\\\Aplicación de Predios\" && rm -rf _comparacion_produccion && echo \"✓ Carpeta temporal _comparacion_produccion eliminada\" && echo \"\" && echo \"=== VERIFICACIÓN FINAL ===\" && ls _comparacion_produccion 2>&1)",
"Bash(xargs -I {} sh -c 'echo \"{}:\"; wc -l {}')",
"Bash(xargs grep -l \"LockService\\\\|worktree\\\\|concurrent\")",
"Bash(grep -v \"//\")"
]
},
"hooks": {
"PreToolUse": [
{
Expand All @@ -21,4 +32,4 @@
}
]
}
}
}
2 changes: 1 addition & 1 deletion ARCHITECTURE_V5.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ARCHITECTURE_V5.md — Sprint 6: Estabilización Post-Línea Cero (Performance, Limpieza y UX)

**Estado:** [PLANIFICACIÓN — investigación completada, código no tocado todavía]
**Estado:** [COMPLETADO Y DESPLEGADO — 2026-08-18. Ver DOCUMENTACION_TECNICA_VIVA.md, Sección 25, para el detalle de la implementación real. Este documento se conserva como registro histórico del plan original.]
**Agente:** Claude Code (Claude Sonnet 5, orquestado con gstack v1.60.1.0).
**Fecha:** 2026-08-18.
**Contexto:** la Línea Cero (Sprint 5) redujo los RTs pendientes de ~9691 a 4690 — éxito confirmado por el usuario en producción. Este sprint no es funcionalidad nueva, es estabilización: 4 puntos críticos de UX/Performance levantados directamente por el usuario tras usar el sistema real.
Expand Down
117 changes: 92 additions & 25 deletions Codigo.js
Original file line number Diff line number Diff line change
Expand Up @@ -1069,42 +1069,81 @@ function getSavedReports(usuario) {
}

function saveReport(nombre, config, filtros, usuario) {
const actualUserEmail = Session.getActiveUser().getEmail();

// ✅ TRY/CATCH 1: PERMISOS (whitelist de roles que pueden crear)
try {
const gestor = new GestorReportes();
return gestor.guardarReporte(nombre, config, filtros, usuario);
} catch (e) {
console.error(`Error en saveReport: ${e.message}`);
return {
success: false,
error: e.message
};
const gestor = new GestorPermisos();
gestor.validarPermiso('REPORTES');

const rol = gestor.obtenerRol(actualUserEmail);
const ROLES_PUEDEN_CREAR_REPORTE = ['Administrador', 'Editor', 'Articulador'];
if (ROLES_PUEDEN_CREAR_REPORTE.indexOf(rol) === -1) {
throw new Error(
`❌ ACCESO DENEGADO — Solo ${ROLES_PUEDEN_CREAR_REPORTE.join(', ')} pueden crear reportes. ` +
`Tu rol es: ${rol}.`
);
}
} catch (ePermiso) {
logAction(actualUserEmail, 'CREAR_REPORTE_DENEGADO', { razon: ePermiso.message });
console.error(`Error en saveReport (permisos): ${ePermiso.message}`);
return { success: false, error: ePermiso.message };
}

// ✅ TRY/CATCH 2: LÓGICA DE NEGOCIO
try {
const gestorReportes = new GestorReportes();
return gestorReportes.guardarReporte(nombre, config, filtros, usuario);
} catch (eLogica) {
console.error(`Error en saveReport (lógica): ${eLogica.message}`);
return { success: false, error: eLogica.message };
}
}

function executeReport(reporteId, usuario) {
const actualUserEmail = Session.getActiveUser().getEmail();

// ✅ TRY/CATCH 1: PERMISOS
try {
const gestor = new GestorReportes();
const resultado = gestor.ejecutarReporte(reporteId, usuario);
const gestor = new GestorPermisos();
gestor.validarPermiso('REPORTES');
} catch (ePermiso) {
logAction(actualUserEmail, 'EJECUTAR_REPORTE_DENEGADO', { razon: ePermiso.message });
console.error(`Error en executeReport (permisos): ${ePermiso.message}`);
return JSON.stringify({ success: false, error: ePermiso.message });
}

// ✅ TRY/CATCH 2: LÓGICA DE NEGOCIO
try {
const gestorReportes = new GestorReportes();
const resultado = gestorReportes.ejecutarReporte(reporteId, usuario);
return JSON.stringify(resultado);
} catch (e) {
console.error(`Error en executeReport: ${e.message}`);
return JSON.stringify({
success: false,
error: e.message
});
} catch (eLogica) {
console.error(`Error en executeReport (lógica): ${eLogica.message}`);
return JSON.stringify({ success: false, error: eLogica.message });
}
}

function deleteReport(reporteId, usuario) {
const actualUserEmail = Session.getActiveUser().getEmail();

// ✅ TRY/CATCH 1: PERMISOS (requiere ELIMINAR, solo Admin)
try {
const gestor = new GestorReportes();
return gestor.eliminarReporte(reporteId, usuario);
} catch (e) {
console.error(`Error en deleteReport: ${e.message}`);
return {
success: false,
error: e.message
};
const gestor = new GestorPermisos();
gestor.validarPermiso('ELIMINAR');
} catch (ePermiso) {
logAction(actualUserEmail, 'ELIMINAR_REPORTE_DENEGADO', { razon: ePermiso.message });
console.error(`Error en deleteReport (permisos): ${ePermiso.message}`);
return { success: false, error: ePermiso.message };
}

// ✅ TRY/CATCH 2: LÓGICA DE NEGOCIO
try {
const gestorReportes = new GestorReportes();
return gestorReportes.eliminarReporte(reporteId, usuario);
} catch (eLogica) {
console.error(`Error en deleteReport (lógica): ${eLogica.message}`);
return { success: false, error: eLogica.message };
}
}

Expand All @@ -1114,21 +1153,45 @@ function deleteReport(reporteId, usuario) {
* ═══════════════════════════════════════════════════════════
*/

/**
* ✅ SPRINT6-FASE-0: Registra acción en auditoría.
* CRÍTICO: Obtiene identidad del usuario SIEMPRE del servidor (Session.getActiveUser),
* NUNCA del parámetro 'user' enviado por cliente (que podría ser falsificado).
* El parámetro 'user' se mantiene por compatibilidad pero es ignorado.
*/
function logAction(user, action, details) {
try {
// ✅ Obtener identidad real del servidor, ignorar el parámetro 'user' del cliente
const actualUserEmail = Session.getActiveUser().getEmail();
const auditoria = new GestorAuditoria();
return auditoria.registrarAccion(user, action, details);
return auditoria.registrarAccion(actualUserEmail, action, details);
} catch (e) {
console.error(`Error en logAction: ${e.message}`);
}
}

function getUserLogs(usuario) {
const requesterEmail = Session.getActiveUser().getEmail();
try {
const gestor = new GestorPermisos();
const esConsultaPropia = (usuario === requesterEmail);

// Validar: si no es consulta propia (incluyendo 'ALL'), requerir 'PERMISOS'
if (!esConsultaPropia) {
gestor.validarPermiso('PERMISOS');
}

// Acceso permitido — proceder a obtener logs
const auditoria = new GestorAuditoria();
const logs = auditoria.obtenerLogsUsuario(usuario);
return JSON.stringify(logs);

} catch (e) {
// Log de intento denegado para auditoría
logAction(requesterEmail, 'ACCESO_LOGS_DENEGADO', {
usuarioSolicitado: usuario,
razonRechazo: e.message
});
console.error(`Error en getUserLogs: ${e.message}`);
return JSON.stringify([]);
}
Expand Down Expand Up @@ -1280,6 +1343,10 @@ function getGeneralStats() {

function initializeSystem() {
try {
// ✅ SPRINT6-FASE-0: Validación RBAC server-side
const gestorPermisos = new GestorPermisos();
gestorPermisos.validarPermiso('ADMIN_SISTEMA'); // Lanza error si no es ADMIN

validateConfig();

const gestor = new GestorDatos();
Expand Down
Loading
Loading