From 8e628f53342153de00558442684842a1ea0fb2d6 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Wed, 28 Jan 2026 00:43:39 +0000 Subject: [PATCH] login changes --- core/__pycache__/urls.cpython-311.pyc | Bin 7264 -> 7470 bytes core/__pycache__/views.cpython-311.pyc | Bin 49794 -> 55391 bytes core/templates/core/login.html | 226 +++------------------ core/templates/core/select_2fa_method.html | 36 ++++ core/templates/core/verify_2fa_otp.html | 38 ++++ core/urls.py | 6 +- core/views.py | 104 +++++++++- locale/ar/LC_MESSAGES/django.mo | Bin 28554 -> 29514 bytes locale/ar/LC_MESSAGES/django.po | 27 +++ 9 files changed, 231 insertions(+), 206 deletions(-) create mode 100644 core/templates/core/select_2fa_method.html create mode 100644 core/templates/core/verify_2fa_otp.html diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index c89533699b57c7e0c0570376523122a28063934d..d6289315d5d71397b1bc147eb9be6f3e4f4e0c2f 100644 GIT binary patch delta 2290 zcmZvcOH3PA6owgmjfaOZV8HmnD+CM#12Gs(nm~AkM<4+l^57BUc!tCd#$+(iqz_nT zkp=Xo6^ksgqDVzms;E&aWsyqRbu&|q+RBS6>Y{6}yXrY};ihUkmaotM{r|ahXXbj| z2fsaM{n~2H%Yvh6?qTfvJ*!iAzvq=qrnpGoT6{w8-dl$`Pd3H#_FmOlP4Rq;;vrel zDU?aP0D@I{lI^JIK*-AaMxf0?yw?~5?Da@)$s<)t zRZ{g)&4C%_fG<0wT1=jTWC?}M{kBJ6I*fCKcd8Njqrh{ylC7b@9U1uWk zQr6n?pH?5V+FIe_%J^(;h}r>_b95GwACQBi4n&=RDmV%t>H_5Cs2fobkc*=pMCSmx zIr<3Ec|abHE+Fa!RLM~vqKkm4IO<0<0H~UyK}17eRp6hd?jP$NgPh^_-_;^+pVn}C`*x`k*C z&>4>A5iI~};b;-jZ9qPb!ibgtwQ>|ebO%ryM^Qu)pmvUAK*BP8V>v6_h0=Ua&WZbB z1Nnp)I&nHBo)zw+vjR@1;UrM5f(#fkiE@qiJ-|N2=vzl8#hndwHqmi!9a#=cuxH`R zjy++nOZqsJ)%1|Q%k2-P%`3^fvDHVV#!uV2ydpIl9I!#k%UaY%HOv%2}&ufg0>b!a$x72x6+o#dUz8G1z!$I0SxIpaFZJ;v=#i@E5$gJ z;9#fkN}_GJv(p9Z(a37}fx^0R8!I(9;8sl+q?AbXet0dSMCBE_WWO9x{AhL<=9-dR zj;+Y7Gl0l;e06YTjCNx>NPn?^mwyXNcsFIOXN&ZU(&_DxV9WlEh&w}%OCHZYncv@1 ziB}_Dop>`Q^xhj@#cn{iH~wmG|JmNI+5NiR|JtFFevS0&q(5WEU?wNaoVzom=2ky1 z(`$oj-;7E^8VTtnl(85A@i^ewm-Q;Y+B2q-@j{J^>tsBWYb5e$pscAr->8d>^kes~ z{B-s3oJzVh(xsEGOtImX&^?#SVmInix>w<9EHmoz<8!$8XZ6Z1^};_E;V=Ro`e)gQ zrP8QdXlHqk#b?x=G*#Yf2^e)R{h>TK)@Rh01hx*(?#9#Y!+DkTXrxCcJ(&@~@JEk* z*Y2!d<~@w5B&d;~PJ)>+78nHbir(NwHQOT%AMs+fpiLi+D;TGtQGpc_;C4(9n)X88*W{KsKNsMP7 zP|r`OWKtuOI+@JeWs!R)5#!1DUeIP|z8GU(oL+ENi1(RTp^Hwpm|$X+?l|2oNhXw& zDgI}v>nxHoZua&DQ=7*NhiCJ2@zAM~4vlo^q$9J%{6`=Be?7ZTSm4v+K=ppT?rM3_ ztC9hY4CrJa^BD_#ezK&h=VrYo@EWdrOe1v3Wfr$tVu&VOZgDu9Z6>-4VuXna`qJeU zCz-fH|8O;kSDBciMQ*P+&BP4t0}*248eMfah_g)0(P!YyGqFH_0+a4Y8AtALRzT0R9H!yIcPN delta 2192 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 PO`J6VZ}3I~&FFstg0CXg delta 8396 zcma)B3vgV;m7OPzW~{H}kG_`ekw3CVw&kB}OEzHRkNlS`#YPJGemg%)})EvvF`YgZdXIi%T(1AdKPxqTe)?{_ERjfXA z@5}@>`SeLBzlDRK+BnAfS-(Nasy>(%RN{8IAM z{Z^PetzGJ_bX$wsqF0{qsIB@8!rgimVVgda@Fsm0VY@z?aF3C%2TpjNrcHWwc*i{I zcZga&j=Gnqb3jd8!@Q5kbH_37C+fU$)B{AFKaQ#rwPqakW_HJgCA<3JJsUN@rS)s5_1642^@c{RI|TDT|FtBJ5-ikpdaL+aS%rAiG(xTx?5wt_A$ z|0H2_MQp4nM8sD4UU8MuCeK$?q`Xre^eU!&q~wIsA}dSp2+kk|p%FNfB6jGAW=3?! zYimag(FCHuTeG!zTnJshQQGL63hwRlo6^eLcrhw?3VOR^Q6m)XvpQ(TWnh^Nx;GN- zjENPf$579rq5ZaD+mTpQ@K&(+epdl^fUgH&0*=dLWq~SQeev*WLUwngx0mL4h&XOa zy-@avqO_&XSIqS$u!23JgRoiX=x+cz0bPIy;1D1RfIOlXz>^(DNdRm>9MA_C0Nf7v zF2O)WI3|oGBjV^HLVBw2j$1dF0`doEi{oIf0ZbOl_<=o?FPawSiiKir66ETe4Xxkkgl*Q3mD3rG_tsE`xIV zvOS5PqKJ(NR&YdMX`R9@BOZ#0P*@m<4LsUfaI!;V8DbZK<2R0l%|4128JDj?<4*v! zfCXU7C3hi*?lNpK4fUMPh_K^Mkr9<2Eh}7zC9({zEBTLVA{vQyv05E(l>9AN>?%N) zvzISPJPUpn9P>E2G;$=W#bZJ|gOoARUII%+}g$nTkI9>$2L|_*a zu+@wLHX!jRcmVH#?_&})>G>k{?((1bd{3fjgS@?|u7J6)>CK`;KHs!E!R|3Fqfha3 zFlV&*zgkWi_}F~dI+Cbj7sX=0c|bd0v{A5Yy$&iz6$-11J$4S&c{Q#h6eE~eMl>E8 z4&`qWLzO~{DwUtDtCPQ8bCsgY+iQQHu)H3?V(fC_?c5XHXZh=3=I=wM}L z_xXSs9}@~Sa<1O^@cHsK4Y(rp>AGEru4`_TN-;Y=&&?Y#C_mURry80FP97y3Z{{d( zmw}Cy9juciv5KVt-e%r36;&@mFnt34BM7ur9w#qq91YoY1ozov4#L8|UY)!z-6-BA z?l$?@#z7Kv)27?9%XoLNFT-2Hmh!xOZA(Nk8ha!u{% zi#$>HA4$hiWhQn!4gwIUVUF(<3a86M$c+LqjvfOkfSq3@QMlih~5Bk`*xoBH#h}@a_ic-_uqh|FC;g-K`UFJ4Kp^N5T}n zbR$lIOngX8XJktoB{->`wl+oi)5F!%<*L2grd?FhZbYn6C>NlRYuJvL zY*Z7UQKOC1i-XR3H|i8&Lq=Hrw)lz|&dIHNPO@J|Uo!>uYy; z+ZX%?h<^q#Tl^ROI7--^5`i!fNV?JbFLH42T;(bGPkYmg6`!Na=!BG4I)$izZKYqM z{R;xe7bg3qQ|n7mCR}448=cGb4RKvSOuf@1ll$%(+1;hGb^r8+iFde{(kd<7AL@;n zk#N88Q1?OlFaC%bUGuoIzj@n)or~#cw(L+i7S&0BSkx9-)b$xi0MAc)KFFW);DVtU ztACZ>_%Hd5L*7Oj5l*-Nz*|bSh8aEu!~jPLNV}s5a^20R+@q|(&GgE_$KCjJT8`?y zfH6+B4>UG^KBuxXIWO5BMFHF0H8)8UUXqvL5(NK%`X)WU@fV#q{{7PT{c`&)|E#n< zTwNho-}+W!4F>)Z!N3$wPJ2a+E;9y_Qj_dorxsjRjzo;3Hk}e2%s>>#T^vfS!v-a> zdPrz-$}emuTQg19&_@f(J?OQ1D3t@bbM+|Ss(+35mks&2V(PWn1=wnsh zC)ex2*^{xZVt`A898*w*BZ;r&J^K8Bfnq11i@+&LFDT?jesM^?t2Y#zXg>@P09#Hm zs(o(8E*p*7O?MDAIMdyt3UFuu3CL)GcdQ80C(*#$#wXorR3}WzInZ(_yAM3e0KC&D zQCb1;0$6vsnN4PIadHu5LL3};0ms4m0N_Cay5(sCu47mf*z8;)1?1Y!-11g#G{~Ss zX=MncDet6i=^V&nOGsVq{!i~lULy}&M$OIQ97g_IKq(gae<&Y(C9*m?Ptj#_v^F18 zh#!FZnCy>MC0JOVf|bC@dnPopRv!m9Yn3%PX2K*|*^pRwqxKyQC76yA{xP1z`rPN) zMa>f?|KbUB|8jWnv1#69ipr7B{!lF5>l*1SnkXlw#oBznv(#x|vHbc>rF_3{y6;hp z@s#|u?=C;qiOlubk+tmZeDVcLMkPCZ9P_GKUiNP`43>8gL!|)U1F&_l0xMC)=%cQL zob&kH%cPsovian==Cg*=2=KVcY=Gn+yU2XMtLF(SjkSv6 zwCS6%LfRjAfS{LM&ks`H-o3@^V-qms&Xe#0F zX8_Zl;U&@olif?Ft0DHuyH796&+KP|{QJ`l-So=rakA-JNtp)c!e3Hvr=X8Ige5UobT(*ALy=7w)=QZoP3(AMy++jW z_Gp#H8i`Q5}s(>@owF-Yp3`X$hk*F1UCe|Q&dwuSmY|vjRf-G zxfD$!wf-@6_1wSZxZ!RQhr7Q#KF!tFZkd0+ zBEiwc9g@azCu_$H-vR6;7^sXy%}CT(8$fn}eERx1k?4_FBy5DlesCQi2+o)&Lh{QZ zUFSMG-LrBZ=3p1#SUC;v>=~^I1EX~Ux&RuW1JDi70V4_&5mXNWpg7TkQZ-80tYNp$ zM9|C{d%<}aAe5PexRJ2=*?H76qe@cax(lT_N2$3qrCvCfReLLjKS+Qs!!ND{c36vs z$l^E=>Zg__S@Gm7O6nV*yf=F%q-h=~--BmdO0xp)TWJ6<4-_eMu^hAS!N6+>Mm;SI zWCxZ6g1PQ<0!E=Xs=}w&C)j1NZ3D%-`N|)lH|s!WtS_nut43W41L(rKE<}lKlcO#B zGzUQVx_egfsl_%7^L5`Io<|Aclr`#1MS^0-O<(|(Z zD?2>P%6^zj@5=7c*CXoAeAT-*tGNG5xIJ-uY~CFLm8O zhotRLA?dm5OFcXIif;z?+40dKh7;&)x`HD>tdWV!wLaGJbMn&VS;`gp&gCV(w=nTj zQh9D3E3_q8E>=UTMgV=;n)qg&(OLlgS-Tn4jky0P{oM(^6d(y$0$2)QL%yCSY8o9V z-Is%tm>(J(rKAahqWs{w**T|)*Gef@Uh(>VhaS(#H(sdAe-uRxP$0i}VQr$CxW>QO zu;^n09y%Qdl#Pr*UYeGljgNDbu}?RgqKtX7$#*kXO%&nd#RDY66WF?sruKeA5^Ohr?IF%Fd*IRTtnKmarjxSw-o604>tWD61aEz|9 zq7>utE|jBG0jLDb0N_i5Q=T5YFHZM{diqurwdp|}PC$_@ZJ@KjSahuv9FCp*G_gBQ z2|@rJstHz(&?$+rIsb49(@$0*o=D8aki)%65uNn-RXkL>U_R(2BU1^ZKJyw~gYg#t zYUQq>IZ9CW4wd@qKv*bG4OMN!u_#Ue?j~@Gj?!^&YrVbcITxd)9*}XOWniRpnO=jI zqh$qPwftX|hXD8*;(j?`=Xn#=w*l`0@Knc7 zUGAgRhp2u9_y*v?pm=U@@9%UQPS6ISjynhE{Vd0_BXg0ClCM^@5j8aIfG)I|S0b{3_FSF;sG`Hg4&(Aa;e+V}sEQ{>50s;>fg Gr2heS{l!KA diff --git a/core/templates/core/login.html b/core/templates/core/login.html index 09f4d96..03b6c07 100644 --- a/core/templates/core/login.html +++ b/core/templates/core/login.html @@ -19,78 +19,32 @@

{{ platform_profile.name|default:"masarX" }}

{% endif %}

{% trans "Welcome Back" %}

-

{% trans "Please login to your account" %}

+

{% trans "Please login with your username and password" %}

- - - -
- -
-
- {% csrf_token %} - {% for field in form %} -
- - {{ field }} - {% if field.errors %} -
{{ field.errors }}
- {% endif %} -
- {% endfor %} - - - - -
+
+ {% csrf_token %} + {% for field in form %} +
+ + {{ field }} + {% if field.errors %} +
{{ field.errors }}
+ {% endif %} +
+ {% endfor %} + + - -
-
-
- - -
-
- -
- -
-
-
- - -
-
- -
- -
-
-
-
+ +
{% trans "Don't have an account?" %} @@ -105,15 +59,6 @@ - - -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/core/templates/core/select_2fa_method.html b/core/templates/core/select_2fa_method.html new file mode 100644 index 0000000..fac553e --- /dev/null +++ b/core/templates/core/select_2fa_method.html @@ -0,0 +1,36 @@ +{% extends "base.html" %} +{% load i18n static %} + +{% block content %} +
+
+
+
+
+

{% trans "Two-Factor Authentication" %}

+

{% trans "Please choose how you would like to receive your verification code." %}

+ +
+ {% csrf_token %} +
+ + + +
+
+ + +
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/verify_2fa_otp.html b/core/templates/core/verify_2fa_otp.html new file mode 100644 index 0000000..77da6bc --- /dev/null +++ b/core/templates/core/verify_2fa_otp.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} +{% load i18n static %} + +{% block content %} +
+
+
+
+
+

{% trans "Verify OTP" %}

+

{% trans "Enter the 6-digit code sent to your selected method." %}

+ +
+ {% csrf_token %} +
+ +
+ +
+ +
+
+ + +
+
+
+
+
+{% endblock %} diff --git a/core/urls.py b/core/urls.py index 685628e..b737738 100644 --- a/core/urls.py +++ b/core/urls.py @@ -5,7 +5,9 @@ from . import api_views urlpatterns = [ path('', views.index, name='index'), - path('login/', auth_views.LoginView.as_view(template_name='core/login.html'), name='login'), + path('login/', views.CustomLoginView.as_view(), name='login'), + path('login/select-method/', views.select_2fa_method, name='select_2fa_method'), + path('login/verify-2fa/', views.verify_2fa_otp, name='verify_2fa_otp'), path('logout/', auth_views.LogoutView.as_view(next_page='/'), name='logout'), # Registration Flow @@ -84,4 +86,4 @@ urlpatterns = [ # Root-level Aliases (for apps hardcoded to /shipments/) path('shipments/', api_views.ParcelListCreateView.as_view(), name='root_shipment_list'), path('shipments//', api_views.ParcelDetailView.as_view(), name='root_shipment_detail'), -] \ No newline at end of file +] diff --git a/core/views.py b/core/views.py index a5d3156..aea9bc3 100644 --- a/core/views.py +++ b/core/views.py @@ -1,3 +1,4 @@ +from django.contrib.auth.views import LoginView from django.shortcuts import render, redirect, get_object_or_404 from django.contrib.auth import login, authenticate, logout from django.contrib.auth.forms import AuthenticationForm @@ -955,4 +956,105 @@ def cancel_parcel(request, parcel_id): parcel.save() messages.success(request, _("Shipment cancelled successfully.")) - return redirect('dashboard') \ No newline at end of file + return redirect('dashboard') +class CustomLoginView(LoginView): + template_name = 'core/login.html' + + def form_valid(self, form): + # Authenticate checks are done by the form + user = form.get_user() + + # Store user ID in session for 2FA step + self.request.session['pre_2fa_user_id'] = user.id + self.request.session.set_expiry(600) # 10 minutes expiry for this session part + + return redirect('select_2fa_method') + +def select_2fa_method(request): + user_id = request.session.get('pre_2fa_user_id') + if not user_id: + return redirect('login') + + try: + user = User.objects.get(id=user_id) + except User.DoesNotExist: + return redirect('login') + + if request.method == 'POST': + method = request.POST.get('method') + code = ''.join(random.choices(string.digits, k=6)) + + # Invalidate old login OTPs + OTPVerification.objects.filter(user=user, purpose='login').delete() + OTPVerification.objects.create(user=user, code=code, purpose='login') + + if method == 'email': + if user.email: + try: + send_html_email( + subject=_("Your Login OTP"), + message=f"Your verification code is: {code}", + recipient_list=[user.email], + title=_("Login Verification") + ) + messages.success(request, _("OTP sent to your email.")) + return redirect('verify_2fa_otp') + except Exception as e: + messages.error(request, _("Failed to send email: ") + str(e)) + else: + messages.error(request, _("No email address associated with this account.")) + + elif method == 'whatsapp': + if hasattr(user, 'profile') and user.profile.phone_number: + if send_whatsapp_message(user.profile.phone_number, f"Your login verification code is: {code}"): + messages.success(request, _("OTP sent to your WhatsApp.")) + return redirect('verify_2fa_otp') + else: + messages.error(request, _("Failed to send WhatsApp message. Please check the logs.")) + else: + messages.error(request, _("No phone number found for this account.")) + + return render(request, 'core/select_2fa_method.html') + +def verify_2fa_otp(request): + user_id = request.session.get('pre_2fa_user_id') + if not user_id: + return redirect('login') + + try: + user = User.objects.get(id=user_id) + except User.DoesNotExist: + return redirect('login') + + if request.method == 'POST': + code = request.POST.get('otp') + + try: + # Find the most recent valid login OTP for this user + otp_record = OTPVerification.objects.filter( + user=user, + purpose='login', + is_verified=False + ).latest('created_at') + + if otp_record.code == code and otp_record.is_valid(): + otp_record.is_verified = True + otp_record.save() + + # ACTUAL LOGIN HAPPENS HERE + login(request, user) + + # Clean up session + if 'pre_2fa_user_id' in request.session: + del request.session['pre_2fa_user_id'] + request.session.set_expiry(None) + + messages.success(request, _("Logged in successfully.")) + return redirect('dashboard') + else: + messages.error(request, _("Invalid or expired OTP.")) + + except OTPVerification.DoesNotExist: + messages.error(request, _("No valid OTP found. Please request a new one.")) + + return render(request, 'core/verify_2fa_otp.html') diff --git a/locale/ar/LC_MESSAGES/django.mo b/locale/ar/LC_MESSAGES/django.mo index 7f90fed1cd95da92bb31f6ee7c481b6bf226fe01..84972a7b2d9fd1ae5516d024bcb5824160009bdf 100644 GIT binary patch delta 8377 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