505 lines
19 KiB
PHP
505 lines
19 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../helpers/session.php';
|
|
require_once __DIR__ . '/../../config/database.php';
|
|
require_once __DIR__ . '/../helpers/crypto.php';
|
|
require_once __DIR__ . '/../helpers/bitacoras.php';
|
|
require_once __DIR__ . '/../helpers/env.php';
|
|
|
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
|
use PHPMailer\PHPMailer\PHPMailer;
|
|
use PHPMailer\PHPMailer\Exception;
|
|
|
|
loadEnv();
|
|
|
|
function enviarCodigoInterno()
|
|
{
|
|
// header('Content-Type: application/json');
|
|
|
|
// Verificar que el usuario esté autenticado
|
|
if (!isset($_SESSION['usuario_id']) || !isset($_SESSION['usuario_email'])) {
|
|
echo json_encode(['success' => false, 'message' => '❌ Sesión no válida. Inicia sesión nuevamente.']);
|
|
exit;
|
|
}
|
|
|
|
$conn = getConnection();
|
|
if (!$conn) {
|
|
echo json_encode(['success' => false, 'message' => '❌ Error de conexión con la base de datos.']);
|
|
exit;
|
|
}
|
|
|
|
$id_usuario = $_SESSION['usuario_id'];
|
|
$email = $_SESSION['usuario_email'];
|
|
$emailEncrypted = encrypt($email);
|
|
|
|
// Verificar que la cuenta esté activa
|
|
$sql = "SELECT activo FROM usuarios_sistema WHERE id_usuario = ?";
|
|
$stmt = sqlsrv_query($conn, $sql, [$id_usuario]);
|
|
|
|
if (!$stmt || !($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))) {
|
|
echo json_encode(['success' => false, 'message' => '❌ Usuario no encontrado.']);
|
|
exit;
|
|
}
|
|
|
|
if ($row['activo'] != 1) {
|
|
echo json_encode(['success' => false, 'message' => '❌ Tu cuenta está inactiva.']);
|
|
exit;
|
|
}
|
|
|
|
// Verificar si ya existe un código activo
|
|
$now = (new DateTime())->format('Y-m-d H:i:s');
|
|
$sqlCheck = "SELECT COUNT(*) AS total FROM recuperacion_password
|
|
WHERE email = ? AND estatus = 0 AND expiracion > ?";
|
|
$checkStmt = sqlsrv_query($conn, $sqlCheck, [$emailEncrypted, $now]);
|
|
$checkRow = sqlsrv_fetch_array($checkStmt, SQLSRV_FETCH_ASSOC);
|
|
|
|
if ($checkRow['total'] > 0) {
|
|
echo json_encode(['success' => false, 'message' => '⚠️ Ya tienes un código activo. Revisa tu correo o espera 10 minutos.']);
|
|
exit;
|
|
}
|
|
|
|
// Generar nuevo código
|
|
$codigo = strval(random_int(100000, 999999));
|
|
$expira = (new DateTime('+10 minutes'))->format('Y-m-d H:i:s');
|
|
$estatus = 0;
|
|
|
|
$insert = "INSERT INTO recuperacion_password (email, codigo, expiracion, estatus) VALUES (?, ?, ?, ?)";
|
|
$params = [$emailEncrypted, $codigo, $expira, $estatus];
|
|
$result = sqlsrv_query($conn, $insert, $params);
|
|
|
|
if (!$result) {
|
|
echo json_encode(['success' => false, 'message' => '❌ Error al generar el código de verificación.']);
|
|
exit;
|
|
}
|
|
|
|
// Establecer variables de sesión para el cambio interno
|
|
$_SESSION['cambio_interno'] = true;
|
|
$_SESSION['codigo_timestamp'] = time();
|
|
|
|
// Agregar estos logs:
|
|
error_log("DEBUG enviarCodigoInterno - SESSION después de establecer variables: " . print_r($_SESSION, true));
|
|
error_log("DEBUG - cambio_interno establecido: " . ($_SESSION['cambio_interno'] ? 'TRUE' : 'FALSE'));
|
|
|
|
// Enviar código por correo
|
|
try {
|
|
$mail = new PHPMailer(true);
|
|
$mail->isSMTP();
|
|
$mail->Host = 'secure.emailsrvr.com';
|
|
$mail->SMTPAuth = true;
|
|
$mail->Username = 'noreply@aduanasoft.com.mx';
|
|
$mail->Password = $_ENV['SMTP_PASS'];
|
|
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
|
|
$mail->Port = 587;
|
|
|
|
$mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | AduanaSoft');
|
|
$mail->addAddress($email);
|
|
$mail->CharSet = 'UTF-8';
|
|
$mail->isHTML(true);
|
|
$mail->Subject = 'Código para cambio de contraseña';
|
|
|
|
$mail->Body = "
|
|
<div style='font-family: Segoe UI, sans-serif; background-color: #f4f6f9; padding: 40px;'>
|
|
<div style='max-width: 600px; margin: auto; background: #fff; border: 1px solid #ddd; border-radius: 10px; overflow: hidden;'>
|
|
<div style='background: linear-gradient(to right, #003366, #0055A5); padding: 20px; text-align: center;'>
|
|
<h2 style='color: white;'>Cambio de contraseña</h2>
|
|
</div>
|
|
<div style='padding: 30px; color: #333; font-size: 16px;'>
|
|
<p>Has solicitado cambiar tu contraseña desde tu cuenta.</p>
|
|
<div style='text-align: center; margin: 30px 0;'>
|
|
<p>Tu código de verificación es:</p>
|
|
<h2 style='color:#333; background: #f8f9fa; padding: 15px; border-radius: 8px; border: 2px dashed #0055A5;'>$codigo</h2>
|
|
</div>
|
|
<p>Este código expirará en 10 minutos.</p>
|
|
<p><strong>Si no solicitaste este cambio, ignora este correo.</strong></p>
|
|
</div>
|
|
<div style='background: #e9ecef; text-align: center; padding: 15px; font-size: 13px; color: #666;'>
|
|
© " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos
|
|
</div>
|
|
</div>
|
|
</div>";
|
|
|
|
$mail->send();
|
|
|
|
// Redirigir directamente desde PHP
|
|
header("Location: /IMPORTADORES/reset/verificarCodigoInternoView");
|
|
exit;
|
|
|
|
} catch (Exception $e) {
|
|
error_log("Error al enviar correo de cambio interno: {$mail->ErrorInfo}");
|
|
|
|
// Solo en caso de error, devolver JSON
|
|
echo json_encode(['success' => false, 'message' => '❌ Error al enviar el correo. Intenta más tarde.']);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
function verificarCodigoInternoView()
|
|
{
|
|
// Verificar si el usuario ya está autenticado
|
|
if (!isset($_SESSION['usuario_id']) || !isset($_SESSION['usuario_email']) || !isset($_SESSION['cambio_interno'])) {
|
|
header("Location: /IMPORTADORES/login");
|
|
exit;
|
|
}
|
|
|
|
// Verificar si el código ya fue verificado
|
|
if (isset($_SESSION['codigo_verificado_interno']) && $_SESSION['codigo_verificado_interno'] === true) {
|
|
header("Location: /IMPORTADORES/reset/cambiarPasswordInternoView");
|
|
exit;
|
|
}
|
|
|
|
// Mostrar la vista de verificación de código interno
|
|
include __DIR__ . '/../../views/reset/verificar_codigo.php';
|
|
}
|
|
|
|
function verificarCodigoInterno()
|
|
{
|
|
header('Content-Type: application/json');
|
|
|
|
$conn = getConnection();
|
|
|
|
$email = $_SESSION['usuario_email'];
|
|
$emailEncrypted = encrypt($email); // aplica aquí también
|
|
|
|
try {
|
|
// Verificar conexión
|
|
if (!isset($conn) || $conn === false) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => '❌ Error de conexión a la base de datos.',
|
|
'intentos_restantes' => max(0, 3 - ($_SESSION['intentos_codigo_interno'] ?? 0))
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
$codigo = trim($_POST['codigo'] ?? '');
|
|
|
|
if (empty($codigo)) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => '❌ Código requerido.',
|
|
'intentos_restantes' => max(0, 3 - ($_SESSION['intentos_codigo_interno'] ?? 0))
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
if (!preg_match('/^\d{6}$/', $codigo)) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => '❌ El código debe tener 6 dígitos numéricos.',
|
|
'intentos_restantes' => max(0, 3 - ($_SESSION['intentos_codigo_interno'] ?? 0))
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
$intentosActuales = $_SESSION['intentos_codigo_interno'] ?? 0;
|
|
|
|
if ($intentosActuales >= 3) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => '❌ Demasiados intentos fallidos. Solicita un nuevo código.',
|
|
'blocked' => true,
|
|
'redirect' => '/IMPORTADORES/seguridad/index',
|
|
'intentos_restantes' => 0
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
if (!isset($_SESSION['usuario_id']) || !isset($_SESSION['usuario_email'])) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => '❌ Sesión inválida. Intenta nuevamente.',
|
|
'blocked' => true,
|
|
'redirect' => '/IMPORTADORES/seguridad/index'
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// Consulta principal para verificar el código
|
|
$sql = "SELECT id, codigo, expiracion
|
|
FROM recuperacion_password
|
|
WHERE email = ? AND estatus = 0 AND expiracion > GETDATE()
|
|
ORDER BY expiracion DESC
|
|
";
|
|
|
|
$stmt = sqlsrv_query($conn, $sql, [$emailEncrypted]);
|
|
|
|
if (!$stmt) {
|
|
throw new Exception("Error en la consulta a la base de datos");
|
|
}
|
|
|
|
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
|
sqlsrv_free_stmt($stmt);
|
|
|
|
if (!$row) {
|
|
$_SESSION['intentos_codigo_interno'] = $intentosActuales + 1;
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => '❌ Código inválido o expirado.',
|
|
'intentos_restantes' => max(0, 3 - $_SESSION['intentos_codigo_interno'])
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
if (!isset($row['codigo'], $row['id'], $row['expiracion'])) {
|
|
throw new Exception("Faltan campos requeridos en la respuesta de la base de datos.");
|
|
}
|
|
|
|
if ($codigo !== $row['codigo']) {
|
|
$_SESSION['intentos_codigo_interno'] = $intentosActuales + 1;
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => '❌ Código incorrecto.',
|
|
'intentos_restantes' => max(0, 3 - $_SESSION['intentos_codigo_interno'])
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
$expiracion = $row['expiracion'];
|
|
$ahora = new DateTime();
|
|
|
|
if ($expiracion instanceof DateTime) {
|
|
$expiracionStr = $expiracion->format('Y-m-d H:i:s');
|
|
|
|
if ($expiracion <= $ahora) {
|
|
$_SESSION['intentos_codigo_interno'] = $intentosActuales + 1;
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => '❌ El código ha expirado.',
|
|
'intentos_restantes' => max(0, 3 - $_SESSION['intentos_codigo_interno'])
|
|
]);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// Marcar el código como usado
|
|
$sqlUpdate = "UPDATE recuperacion_password SET estatus = 1 WHERE id = ?";
|
|
$stmtUpdate = sqlsrv_query($conn, $sqlUpdate, [$row['id']]);
|
|
|
|
if (!$stmtUpdate) {
|
|
throw new Exception("Error al actualizar el estado del código");
|
|
}
|
|
|
|
sqlsrv_free_stmt($stmtUpdate);
|
|
|
|
// Marcar como verificado y limpiar intentos
|
|
$_SESSION['codigo_verificado_interno'] = true;
|
|
unset($_SESSION['intentos_codigo_interno']);
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'message' => '✅ Código verificado correctamente.',
|
|
'redirect' => '/IMPORTADORES/reset/cambiarPasswordInternoView'
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => '❌ Error interno del servidor. Intenta nuevamente.',
|
|
'intentos_restantes' => max(0, 3 - ($_SESSION['intentos_codigo_interno'] ?? 0))
|
|
]);
|
|
exit;
|
|
} finally {
|
|
// Cerrar conexión si existe
|
|
if (isset($conn) && $conn !== false) {
|
|
sqlsrv_close($conn);
|
|
}
|
|
}
|
|
}
|
|
|
|
function cambiarPasswordInternoView()
|
|
{
|
|
// Verificar si el usuario ya está autenticado y el código verificado
|
|
if (!isset($_SESSION['usuario_id']) || !isset($_SESSION['usuario_email']) ||
|
|
!isset($_SESSION['cambio_interno']) || !isset($_SESSION['codigo_verificado_interno'])) {
|
|
header("Location: /IMPORTADORES/login");
|
|
exit;
|
|
}
|
|
|
|
// Mostrar la vista de cambio de contraseña interno
|
|
include __DIR__ . '/../../views/reset/change_password.php';
|
|
}
|
|
|
|
function cambiarPasswordInterno()
|
|
{
|
|
header('Content-Type: application/json'); // ⬅️ AGREGAR ESTA LÍNEA
|
|
|
|
// Verificar autorización completa
|
|
if (!isset($_SESSION['usuario_id']) || !isset($_SESSION['usuario_email']) ||
|
|
!isset($_SESSION['cambio_interno']) || !isset($_SESSION['codigo_verificado_interno'])) {
|
|
echo json_encode(['success' => false, 'message' => '❌ No tienes autorización para cambiar la contraseña.']);
|
|
exit;
|
|
}
|
|
|
|
$conn = getConnection();
|
|
if (!$conn) {
|
|
echo json_encode(['success' => false, 'message' => '❌ Error de conexión con la base de datos.']);
|
|
exit;
|
|
}
|
|
|
|
$passwordActual = $_POST['password_actual'] ?? '';
|
|
$passwordNueva = $_POST['password_nueva'] ?? '';
|
|
$confirmarPassword = $_POST['confirmar_password'] ?? '';
|
|
|
|
// Validaciones
|
|
if (empty($passwordActual)) {
|
|
echo json_encode(['success' => false, 'message' => '❌ La contraseña actual es requerida.']);
|
|
exit;
|
|
}
|
|
|
|
if (strlen($passwordNueva) < 6) {
|
|
echo json_encode(['success' => false, 'message' => '❌ La nueva contraseña debe tener al menos 6 caracteres.']);
|
|
exit;
|
|
}
|
|
|
|
if ($passwordNueva !== $confirmarPassword) {
|
|
echo json_encode(['success' => false, 'message' => '❌ Las contraseñas nuevas no coinciden.']);
|
|
exit;
|
|
}
|
|
|
|
if ($passwordActual === $passwordNueva) {
|
|
echo json_encode(['success' => false, 'message' => '❌ La nueva contraseña debe ser diferente a la actual.']);
|
|
exit;
|
|
}
|
|
|
|
$id_usuario = $_SESSION['usuario_id'];
|
|
$email = $_SESSION['usuario_email'];
|
|
$emailEncrypted = encrypt($email);
|
|
|
|
// Verificar contraseña actual
|
|
$sql = "SELECT password_hash FROM usuarios_sistema WHERE id_usuario = ?";
|
|
$stmt = sqlsrv_query($conn, $sql, [$id_usuario]);
|
|
|
|
if (!$stmt || !($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))) {
|
|
echo json_encode(['success' => false, 'message' => '❌ Error al verificar la contraseña actual.']);
|
|
exit;
|
|
}
|
|
|
|
if (!password_verify($passwordActual, $row['password_hash'])) {
|
|
echo json_encode(['success' => false, 'message' => '❌ La contraseña actual es incorrecta.']);
|
|
exit;
|
|
}
|
|
|
|
// Actualizar contraseña
|
|
$hashNueva = password_hash($passwordNueva, PASSWORD_BCRYPT);
|
|
$sqlUpdate = "UPDATE usuarios_sistema SET password_hash = ? WHERE id_usuario = ?";
|
|
$stmtUpdate = sqlsrv_prepare($conn, $sqlUpdate, [$hashNueva, $id_usuario]);
|
|
|
|
if (!$stmtUpdate || !sqlsrv_execute($stmtUpdate)) {
|
|
echo json_encode(['success' => false, 'message' => '❌ Error al actualizar la contraseña.']);
|
|
exit;
|
|
}
|
|
|
|
// Registrar en bitácora
|
|
$ip = $_SERVER['REMOTE_ADDR'] ?? 'N/A';
|
|
registrarBitacora($conn, $id_usuario, $email, $ip, 1, 'Cambio de contraseña interno');
|
|
|
|
// Enviar correo de confirmación
|
|
try {
|
|
$mail = new PHPMailer(true);
|
|
$mail->isSMTP();
|
|
$mail->Host = 'secure.emailsrvr.com';
|
|
$mail->SMTPAuth = true;
|
|
$mail->Username = 'noreply@aduanasoft.com.mx';
|
|
$mail->Password = $_ENV['SMTP_PASS'];
|
|
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
|
|
$mail->Port = 587;
|
|
|
|
$mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | AduanaSoft');
|
|
$mail->addAddress($email);
|
|
$mail->CharSet = 'UTF-8';
|
|
$mail->isHTML(true);
|
|
$mail->Subject = 'Contraseña actualizada exitosamente';
|
|
|
|
$fechaHora = date('d/m/Y H:i:s');
|
|
$mail->Body = "
|
|
<div style='font-family: Segoe UI, sans-serif; background-color: #f4f6f9; padding: 40px;'>
|
|
<div style='max-width: 600px; margin: auto; background: #fff; border: 1px solid #ddd; border-radius: 10px; overflow: hidden;'>
|
|
<div style='background: linear-gradient(to right, #003366, #0055A5); padding: 20px; text-align: center;'>
|
|
<h2 style='color: white;'>Contraseña actualizada</h2>
|
|
</div>
|
|
<div style='padding: 30px; color: #333; font-size: 16px;'>
|
|
<p>Hola,</p>
|
|
<p>Te confirmamos que tu contraseña ha sido <strong>cambiada exitosamente</strong> desde tu cuenta.</p>
|
|
<p><strong>Fecha y hora:</strong> {$fechaHora}</p>
|
|
<p><strong>IP:</strong> {$ip}</p>
|
|
<div style='background: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 5px; margin: 20px 0;'>
|
|
<p style='margin: 0; color: #856404;'><strong>⚠️ Importante:</strong> Si no realizaste este cambio, contacta inmediatamente al área de soporte.</p>
|
|
</div>
|
|
</div>
|
|
<div style='background: #e9ecef; text-align: center; padding: 15px; font-size: 13px; color: #666;'>
|
|
© " . date('Y') . " SIIH · Sistema Integral para Importadores de Hidrocarburos
|
|
</div>
|
|
</div>
|
|
</div>";
|
|
|
|
$mail->send();
|
|
} catch (Exception $e) {
|
|
error_log("Error al enviar correo de confirmación interno: {$mail->ErrorInfo}");
|
|
// No fallar el proceso por error de email, solo registrar el error
|
|
}
|
|
|
|
// Limpiar variables de sesión del proceso
|
|
unset($_SESSION['cambio_interno']);
|
|
unset($_SESSION['codigo_verificado_interno']);
|
|
unset($_SESSION['codigo_timestamp']);
|
|
|
|
echo json_encode([
|
|
'success' => true,
|
|
'message' => '✅ Contraseña actualizada exitosamente.',
|
|
'redirect' => '/IMPORTADORES/login' // Redirigir a la página de seguridad
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
function cancelarCambioInterno()
|
|
{
|
|
header('Content-Type: application/json');
|
|
|
|
if (isset($_SESSION['cambio_interno'])) {
|
|
$conn = getConnection();
|
|
|
|
if ($conn && isset($_SESSION['usuario_email'])) {
|
|
// Invalidar códigos activos
|
|
$emailEncrypted = encrypt($_SESSION['usuario_email']);
|
|
$sql = "UPDATE recuperacion_password SET estatus = 1 WHERE email = ? AND estatus = 0";
|
|
$stmt = sqlsrv_query($conn, $sql, [$emailEncrypted]);
|
|
if ($stmt) sqlsrv_free_stmt($stmt);
|
|
}
|
|
|
|
// Limpiar sesión
|
|
unset($_SESSION['cambio_interno']);
|
|
unset($_SESSION['codigo_verificado_interno']);
|
|
unset($_SESSION['intentos_codigo_interno']);
|
|
unset($_SESSION['codigo_timestamp']);
|
|
}
|
|
|
|
echo json_encode(['success' => true, 'message' => 'Proceso cancelado.']);
|
|
}
|
|
|
|
// Función para obtener el estado actual (para mostrar en la vista)
|
|
function obtenerEstadoDosFactores()
|
|
{
|
|
$conn = getConnection();
|
|
|
|
// CORRIGIDO: Cambiar id_usuario por usuario_id
|
|
$id_usuario = $_SESSION['usuario_id'] ?? null;
|
|
if (!$id_usuario) {
|
|
return 0; // valor por defecto si no hay sesión
|
|
}
|
|
|
|
$sql_dos_factores = "SELECT dos_factores FROM usuarios_sistema WHERE id_usuario = ?";
|
|
$params = [$id_usuario];
|
|
$stmt_dos_factores = sqlsrv_prepare($conn, $sql_dos_factores, $params);
|
|
|
|
$dos_factores_estado = 0; // valor por defecto
|
|
|
|
if ($stmt_dos_factores && sqlsrv_execute($stmt_dos_factores)) {
|
|
if ($row = sqlsrv_fetch_array($stmt_dos_factores, SQLSRV_FETCH_ASSOC)) {
|
|
$dos_factores_estado = (int)$row['dos_factores'];
|
|
}
|
|
}
|
|
|
|
sqlsrv_free_stmt($stmt_dos_factores);
|
|
sqlsrv_close($conn);
|
|
|
|
return $dos_factores_estado;
|
|
} |