From 797cfaa1298dfee7bb830b07a393e329ee18c947 Mon Sep 17 00:00:00 2001 From: adevopg <133045315+adevopg@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:39:05 +0100 Subject: [PATCH] Implementado Solicitar Token de Seguridad (Web) --- home/__pycache__/models.cpython-313.pyc | Bin 8801 -> 9605 bytes home/__pycache__/views.cpython-313.pyc | Bin 43782 -> 46989 bytes home/migrations/0005_securitytoken.py | 24 +++ .../0005_securitytoken.cpython-313.pyc | Bin 0 -> 1271 bytes home/models.py | 14 ++ .../nw-js-handlers/security_token_response.js | 60 +++++++ home/templates/emails/activation.html | 18 +- home/templates/emails/security_token.html | 163 ++++++++++++++++++ home/templates/partials/head.html | 1 + home/templates/partials/security_token.html | 5 +- home/views.py | 104 ++++++++++- 11 files changed, 373 insertions(+), 16 deletions(-) create mode 100644 home/migrations/0005_securitytoken.py create mode 100644 home/migrations/__pycache__/0005_securitytoken.cpython-313.pyc create mode 100644 home/static/nw-themes/nw-ryu/nw-js-handlers/security_token_response.js create mode 100644 home/templates/emails/security_token.html diff --git a/home/__pycache__/models.cpython-313.pyc b/home/__pycache__/models.cpython-313.pyc index d1d3701490c8887633441a8549e1958aea19804b..09bf542d878d11514aaf98a0f3025621ee8e9a40 100644 GIT binary patch delta 3081 zcma)8U2GIp6y7P_|Nri8ce_8`=@uwkXo0o_h?QDeiqKLmTlvxII?N91K)X}!%vNYL ztqDp@G^M!_9wZ7+!UKs(4G)I+;-e4NLegdoiM|+{K!O1h@yN(49F+6^T44%1VKt7P1xL zUpwE990G1s-3;)7X~i8_b|;qHiR%Hn*_t%uYzH|FRJPva5;Sbz>vF}u5lBpYbejhN{ukNEFZ_{ zeNVD04W#ZUPUav{a!^jyRUX055jtp(cf?S6gkJJ~Y3WAGF*@Xn`i|qUOcb5+HTjHk zMvEVTX^f#p@)|;Ajs4+E&g@0g8wme-ZaCrZ8XLpcT7)+NbRUE{D;FT6i=u|9K~OFc z5ULiRh*lAubki&Tj3HFSLR;|EWw_VbX=BS^&4V3jf2pYPNS~f2$-$&)f1gs&q=KAN z_`FnrG8-otq-oo*G9~awm80?&jl{iE*#~?=r7^c@+Rb~E`$kx2UlZ7{4R`is|>v@E9Z1K z8=NT$1qkUmqYv2`H!c@W>ke5|3R!VnP}Rz`r~{YmI+;S}2&42`WP)p2@HVt^ZCPB% z3*X8UP(MU^TOc_#T|=J7*{m?0svo^UPeys}IK2=(&-GAOj8Cy`I!a$d^;HI9I2Psd zRF3T$Dm&3Y^+2$?VSDYvEqsK4!O0^g*m)H#^;rnN6O|u64Z12!I0? zfb-}IAxnGW)5b}k$CF-Ii4SwBQ(#&3;~eZtt1lq%0HY)b2qAIP7aPBKl`Evp$u>@- zBgt(oW#a0qi)*3RlYQI>y`PNiWV^S3Yw{_=6@44Bja09*wA=Bxc*3|$+#x@Xbbv1sck?*goNW}f|n!m8&c_+Zro7Y|A_ znkB|ncfjo`!+i)BgdW%lLLC%Eis zTJkjA@ifowUvbqfyW&f(_;v0FSL^IPdNCf_v|-7=;f}v$_TY*?yzFmV^0(dbZ=F5( zq?R*x8sgA%IzsR9CugF$Q$iuHv_m!HUtzmY)ZS{JP;%0^N~|zVx2lpzBu&-LhmbY? z5VwPc{b;Z&oZWDQ-2pNhSS7K{XDP&Vq|m?;7VAQ{b;BCPamlrZd=2lI1L`9EyeVwC kXWIOu$wO~7U5!09Ik{o(aoomrUL3eQG(Ysj1ViTKFG;nQo&W#< delta 2562 zcma)8OKcle6wRcz3-!eJ-iq32bE_Tntxkwy#yT29OP&%^g^CLkPBZ$K7;49GWs0bmbITWEE zes-wBAWS>c{X-NW5)2TyZE%C`Tz7GY8>S=u@Tc=uybrxig+hD;q~Q{aTTs!*sKtWZdxmt_b<0=m;e*2{)z zag1Pu;01ys1jh*`5KNz<)@7#EMrBQ}GI|R;Mer1@ak3Hg!D`QhMQfbXdeE~cq+!iH z;W|w*KG<;^L;PruY+tjT4UBmP1raWKPTUb`n|0$*#~Yj8^Ey+tV)c^x4!?)llbg-w zX6NSFtF-@s5F5j#>2I)dftX3GD0OBviLow5n1+8n7c5$naCE2!UwiNRxM(d_^S+;^ z(sZ;(eY+-A>!?<}8`t9D{3P-!+fZ3vEvvGD5q5e`K;EAl6fwL-G}uNI_Wi@NoW&J- zOOK=|Os&^VM@?r2yG&a zr63udWx7rW6O6-F=$e)6>+m@AaD0k9HxM?XbFHEg6}2qNR=crf@*h$H9`z3OT9bi1 zy#!pCGzr622-+GLX2O}78FH-=hy+&IKaT{QH7mep;rL0S%-$mCSQ6{xC76QW!g-79 z`u=pJEUe9uizW}BK|JF`8fSo&@q=VH&tlMKILC$76kRN;Rh3C)F2|;OLEn(X8~P1x zOyEY9a+JXkO^e(=e5o5d5Ggw!`gN3@1~<3XCORIwR5%Rt9@J z-}@>>y-UEm`!?K54j_ZwWcpag>}^mu!6N*gtXTKnplQ2b@AnJB8oZZE#3(y#n&5!O zQWm~UiNZ4ckb3{kbLh%COn0NMvZ_>3%^wc13^2WNS<)(2=d_WffLP87 zSmc}RHdf(5dh&1^_jOiamhQ+Yy+%*>2uKyvE6K9ns2T-L;o_JB~8Qqqk{{=FjYh|OkwmDZq5*h2uqB< z&sm&@@X^s9@!!akPcUeilLCXC%=n8G*-Cr7GwI{-M<#cb)AYA=M^>6*I!aQ#WRY^w zNuLnhC-{`$Ssz~@2SEw$iaS2hUS^~4=-ACw=M^1?mG#eKoP3i6^gZCyj*kJ}1y&Nc uKKw|kT_zF?Aeip66Gv477$MW diff --git a/home/__pycache__/views.cpython-313.pyc b/home/__pycache__/views.cpython-313.pyc index f7ac9fb8f9cf306891a330f1d17e3e6b7eb5dffe..ecb4dea5f72a520163bf3cfd79f712e1fbc45110 100644 GIT binary patch delta 9150 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 A!~g&Q delta 6626 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 BtzQ5D 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 0000000000000000000000000000000000000000..385b0f491cceab3c596493abf8ec03058c4b4591 GIT binary patch literal 1271 zcmZ`(O-vg{6rNq%i@n&eCJ>NC<0wK=>_~wm5QU_rNEAGM9|yneV;#&3p5H?2AIdMzCJI zTdv(oA@r{nl9d^Pi!}hhA{V*FGqi08W7`zwbu@==AlED*H{}_-R&r@{u7E$iM5>mq zWIkwEF_qn5H9;f`d8DjH7|?p;Yg4kuhv4F>p@Vuz7%mc~YlxIg*?~@@*>i!-!%|AoN%ZESLov- zJW%$~872}e1OGfc!XyZkP2V<|pb^-V&6-*VQlyiLJzK9Ctq0NX=y_#QWpNx1V`XE= zBWTqr;5^1}TBNS&AWdTYnu$o(8K*qd=72US4=DE;jf9>{HNGBax1%Req_rmh9jiTo zyf<|;mEYo$)~Farx%C4wgoO<#${uUbfo0|?xU2%CPD2uuYD_BTx6(8~uPh z==KVZhHZ??ey4dX^hCs)2GMV@{et=y%6;2UnbX$K-}h~lyVA>E>t?T=7q9Fht z^=@0-O5Xe໪NOYR&mirX{DO4n?_tpHNluLl#bCT@cWe;6p5hpnG74C9l9 XjLQGklwrNMd(OhCv+xmt5a0d`EIdw= literal 0 HcmV?d00001 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): """