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 @@
- +
@@ -116,10 +116,10 @@ @@ -132,7 +132,7 @@

SÍGUENOS EN

- - - - + + + +
FbIgTwYtFbIgTwYt
- +
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 @@ +
+ + + + + + + + + + + + + +
+
+ +

Términos y Condiciones | Política de Reembolso | Política de Privacidad
{{ NOMBRE_SERVIDOR }} © {{ ANIO_ACTUAL }}

+ + + +
+ + + + +
+ + + + +
+ + + + + +
+ + + + +

TOKEN DE SEGURIDAD

+ + + + + +
+ + + + +
+ + + + +
+
+ + + + +
+ + + + + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +

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

+ + + + + + + +
FbIgTwYt
+ + + + +
+ + + + +

Términos y Condiciones | Política de Reembolso | Política de Privacidad
{{ NOMBRE_SERVIDOR }} © {{ ANIO_ACTUAL }}

+ +
+ +
+ +
\ 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 }}


-
+ + {% csrf_token %}

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): """