diff --git a/home/__pycache__/models.cpython-313.pyc b/home/__pycache__/models.cpython-313.pyc
index d1d3701..09bf542 100644
Binary files a/home/__pycache__/models.cpython-313.pyc and b/home/__pycache__/models.cpython-313.pyc differ
diff --git a/home/__pycache__/views.cpython-313.pyc b/home/__pycache__/views.cpython-313.pyc
index f7ac9fb..ecb4dea 100644
Binary files a/home/__pycache__/views.cpython-313.pyc and b/home/__pycache__/views.cpython-313.pyc differ
diff --git a/home/migrations/0005_securitytoken.py b/home/migrations/0005_securitytoken.py
new file mode 100644
index 0000000..7759b59
--- /dev/null
+++ b/home/migrations/0005_securitytoken.py
@@ -0,0 +1,24 @@
+# Generated by Django 5.1.2 on 2024-11-13 10:45
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('home', '0004_alter_accountactivation_salt_and_more'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='SecurityToken',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('user_id', models.IntegerField()),
+ ('token', models.CharField(max_length=6)),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('expires_at', models.DateTimeField()),
+ ('ip_address', models.GenericIPAddressField()),
+ ],
+ ),
+ ]
diff --git a/home/migrations/__pycache__/0005_securitytoken.cpython-313.pyc b/home/migrations/__pycache__/0005_securitytoken.cpython-313.pyc
new file mode 100644
index 0000000..385b0f4
Binary files /dev/null and b/home/migrations/__pycache__/0005_securitytoken.cpython-313.pyc differ
diff --git a/home/models.py b/home/models.py
index 13ad8be..26947d8 100644
--- a/home/models.py
+++ b/home/models.py
@@ -4,6 +4,8 @@ from django_ckeditor_5.fields import CKEditor5Field
from django.core.exceptions import ValidationError
from django.utils import timezone
import uuid
+from django.contrib.auth.models import User
+import secrets
class Noticia(models.Model):
titulo = models.CharField(max_length=200)
@@ -114,4 +116,16 @@ class AccountActivation(models.Model):
def is_expired(self):
return timezone.now() > self.created_at + timezone.timedelta(hours=1)
+
+class SecurityToken(models.Model):
+ user_id = models.IntegerField() # Referencia al ID del usuario en `acore_auth`
+ token = models.CharField(max_length=6)
+ created_at = models.DateTimeField(auto_now_add=True)
+ expires_at = models.DateTimeField()
+ ip_address = models.GenericIPAddressField()
+
+ def __str__(self):
+ return f"Token for user_id {self.user_id}"
+
+
\ No newline at end of file
diff --git a/home/static/nw-themes/nw-ryu/nw-js-handlers/security_token_response.js b/home/static/nw-themes/nw-ryu/nw-js-handlers/security_token_response.js
new file mode 100644
index 0000000..55a0afb
--- /dev/null
+++ b/home/static/nw-themes/nw-ryu/nw-js-handlers/security_token_response.js
@@ -0,0 +1,60 @@
+$(function() {
+ $('.sec-token-button').on('click', function(e) {
+ e.preventDefault();
+
+ var button = $(this);
+ var originalText = button.html();
+ var data = $("#uw-sec-token-form").serialize();
+
+ // Cambiar el texto del botón a "Solicitando token"
+ changeButton(button, 'Solicitando token');
+
+ $.ajax({
+ type: 'POST',
+ url: '',
+ data: data,
+ dataType: 'json',
+ success: function(response) {
+ $("#sec-token-response").html(response.message).hide().slideDown();
+
+ if (response.success) {
+ // Actualizar la fecha del token en la página si se ha generado correctamente
+ if (response.token_date) {
+ $("#token-date").text(response.token_date);
+ }
+
+ // Cambiar el texto del botón a "Token enviado"
+ button.html("Token enviado").css("color", "#d79602");
+
+ // Restablecer el botón después de 5 segundos
+ setTimeout(function() {
+ restoreButton(button, originalText);
+ $("#sec-token-response").slideUp(function() {
+ $("#sec-token-response").empty();
+ });
+ }, 5000);
+ } else {
+ // Restablecer el botón y ocultar el mensaje en caso de error
+ setTimeout(function() {
+ $("#sec-token-response").slideUp(function() {
+ $("#sec-token-response").empty();
+ restoreButton(button, originalText);
+ });
+ }, 5000);
+ }
+ },
+ error: function() {
+ alert("Algo ha salido mal. Por favor, intenta más tarde.");
+ window.location.reload();
+ }
+ });
+ });
+});
+
+function changeButton(button, text) {
+ button.prop('disabled', true).html(text);
+}
+
+function restoreButton(button, originalText) {
+ button.prop('disabled', false).html(originalText).css("color", "");
+}
diff --git a/home/templates/emails/activation.html b/home/templates/emails/activation.html
index d1ccc39..48f8fb2 100644
--- a/home/templates/emails/activation.html
+++ b/home/templates/emails/activation.html
@@ -4,7 +4,7 @@
- |
+ |
@@ -48,14 +48,14 @@
-  |
+  |
|
|
- |
+ |
@@ -108,7 +108,7 @@
-
+
SÍGUENOS EN |
@@ -116,10 +116,10 @@
|
@@ -132,7 +132,7 @@
-
+
Términos y Condiciones | Política de Reembolso | Política de Privacidad {{ NOMBRE_SERVIDOR }} © {{ ANIO_ACTUAL }} |
diff --git a/home/templates/emails/security_token.html b/home/templates/emails/security_token.html
new file mode 100644
index 0000000..78fc381
--- /dev/null
+++ b/home/templates/emails/security_token.html
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  |
+
+ |
+
+ |
+
+
+
+
+
+
+
+
+ Token de Seguridad |
+
+
+ Alguien desde la IP {{ ip_address }} ha solicitado el Token de seguridad para la cuenta {{ username }}. Si no has sido tú, te recomendamos cambiar la contraseña de tu cuenta.
Tus datos de cuenta son: |
+
+
+ Cuenta: {{ username }} Token de Seguridad: {{ token }}
|
+
+
+ Recuerda que este Token es sensible a mayúsculas y minúsculas. Al usarlo, escríbelo exactamente de la misma forma que aparece aquí. |
+
+
+ |
+ |
+
+
+ ¡No compartas el Token de Seguridad con nadie!
Atentamente, el Equipo de {{ NOMBRE_SERVIDOR }}. |
+
+ |
+
+ |
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SÍGUENOS EN |
+
+
+ |
+ |
+
+ |
+
+ |
+
+
+ |
+ |
+
+ |
+
+ |
+
+
+
+ ​
+
+
+
+
\ No newline at end of file
diff --git a/home/templates/partials/head.html b/home/templates/partials/head.html
index d858c7d..0f29e79 100644
--- a/home/templates/partials/head.html
+++ b/home/templates/partials/head.html
@@ -48,6 +48,7 @@
+
diff --git a/home/templates/partials/security_token.html b/home/templates/partials/security_token.html
index ff310fc..ecfbcb5 100644
--- a/home/templates/partials/security_token.html
+++ b/home/templates/partials/security_token.html
@@ -24,9 +24,10 @@
Fecha de solicitud de Token de seguridad:
- Sin solicitar
+ {{ token_date }}
-
diff --git a/home/views.py b/home/views.py
index f6564f4..90a50aa 100644
--- a/home/views.py
+++ b/home/views.py
@@ -10,7 +10,7 @@ from django.db import connections
from django.contrib import messages
from django.contrib.auth import logout
from django import forms
-from .models import Noticia, ClienteCategoria, ServerSelection, RecruitAFriend, DownloadClientPage, ContentCreator, RecruitReward, ClaimedReward, AccountActivation
+from .models import Noticia, ClienteCategoria, ServerSelection, RecruitAFriend, DownloadClientPage, ContentCreator, RecruitReward, ClaimedReward, AccountActivation, SecurityToken
from django.views.decorators.csrf import csrf_exempt
import logging
from datetime import datetime, timedelta
@@ -20,6 +20,8 @@ from django.utils import timezone
from .library_correo import enviar_correo
from django.utils.crypto import get_random_string
from django.conf import settings
+import secrets
+
# Configuración del logger
logger = logging.getLogger(__name__)
@@ -584,7 +586,7 @@ def my_account(request):
# Si la sesión no tiene el usuario, redirigir al inicio de sesión
if not username:
- return redirect('login')
+ return redirect('log-in')
is_logged_in = True # Si el usuario está en la sesión, asumimos que está conectado
@@ -781,12 +783,104 @@ def change_password_view(request):
return render(request, 'account/change_password.html')
+
+
+def get_user_from_acore(username):
+ """
+ Obtiene la información del usuario desde la base de datos `acore_auth`.
+ """
+ with connections['acore_auth'].cursor() as cursor:
+ cursor.execute("SELECT id, email FROM account WHERE username = %s", [username])
+ result = cursor.fetchone()
+ if result:
+ return {'id': result[0], 'email': result[1]}
+ return None
+
def security_token_view(request):
+ # Verificar si el usuario está autenticado mediante la sesión
+ username = request.session.get('username')
+ if not username:
+ return redirect('login')
+
+ # Obtener el usuario desde la base de datos de `acore_auth`
+ user_data = get_user_from_acore(username)
+ if not user_data:
+ return redirect('login')
+
+ user_id = user_data['id']
+ email = user_data['email']
+ ip_address = request.META.get('REMOTE_ADDR')
+
+ # Verificar si existe un token activo para mostrar la fecha en la plantilla
+ existing_token = SecurityToken.objects.filter(user_id=user_id).first()
+
+ # Obtener la fecha del token o "Sin solicitar" si no hay token
+ token_date = "Sin solicitar"
+ if existing_token:
+ token_date = existing_token.created_at.strftime('%H:%M:%S %d-%m-%Y')
+
+ if request.method == 'POST':
+ # Verificar si ya existe un token activo y si ha caducado
+ if existing_token and existing_token.expires_at > timezone.now():
+ remaining_days = (existing_token.expires_at - timezone.now()).days
+ return JsonResponse({
+ 'success': False,
+ 'message': f'Sólo puedes solicitar un nuevo Token de seguridad cada 7 días, faltan {remaining_days} días para generar un nuevo Token.'
+ })
+
+ # Generar un nuevo token
+ token = secrets.token_urlsafe(4)[:6]
+ expires_at = timezone.now() + timedelta(days=7)
+
+ # Eliminar tokens anteriores del usuario
+ SecurityToken.objects.filter(user_id=user_id).delete()
+
+ # Crear y guardar el nuevo token
+ new_token = SecurityToken.objects.create(
+ user_id=user_id,
+ token=token,
+ ip_address=ip_address,
+ expires_at=expires_at
+ )
+
+ # Enviar el correo con el token al usuario
+ send_security_token_email(email, username, token, ip_address)
+
+ # Obtener la nueva fecha del token
+ new_token_date = new_token.created_at.strftime('%H:%M:%S %d-%m-%Y')
+
+ # Responder con un mensaje exitoso y la nueva fecha
+ return JsonResponse({
+ 'success': True,
+ 'message': f'Se ha enviado el Token de seguridad al correo {email}.',
+ 'token_date': new_token_date
+ })
+
+ # Pasar `token_date` al contexto de la plantilla
+ return render(request, 'account/security_token.html', {
+ 'token_date': token_date
+ })
+
+def send_security_token_email(email, username, token, ip_address):
"""
- Vista para la página de 'Security Token'.
- Simplemente renderiza un template con información relevante.
+ Envía un correo electrónico con el token de seguridad.
"""
- return render(request, 'account/security_token.html')
+ context = {
+ 'username': username,
+ 'token': token,
+ 'ip_address': ip_address,
+ 'NOMBRE_SERVIDOR': settings.NOMBRE_SERVIDOR
+ }
+
+ # Llamada a tu función personalizada `enviar_correo`
+ enviar_correo(
+ subject=f'Token de seguridad de la cuenta {username} - {settings.NOMBRE_SERVIDOR}',
+ to_email=email,
+ template='emails/security_token.html', # Asegúrate de que este template exista
+ context=context
+ )
+
+
def change_email_view(request):
"""
| | | | |