
    /i'                    @   d dl mZ d dlZd dlZd dlZd dlmZ d dlmZ d dl	m
Z
mZ d dlmZmZmZ d dlmZ d dlmZ d d	lmZmZmZmZmZ d d
lmZ d dlmZ d dlmZ  ej@                  e!      Z"ddZ#ddZ$ddZ%ddZ&ddZ'ddZ(ddZ)ddZ*ddZ+d dZ,d!dZ-d"dZ.y)#    )annotationsN	timedelta)EmailMessage)HTTPExceptionstatus)selectupdatedelete)AsyncSession)settings)create_tokenhash_passwordhash_refresh_tokenutcnowverify_password)RefreshToken)PasswordResetToken)Userc                 6    t        t        j                        S )Nminutes)r   r   ACCESS_TOKEN_EXPIRE_MINUTES     ?/var/www/html/marco-python-backend/app/services/auth_service.py_access_expiresr      s    XAABBr   c                 6    t        t        j                        S )N)days)r   r   REFRESH_TOKEN_EXPIRE_DAYSr   r   r   _refresh_expiresr!       s    (<<==r   c                     t        d      S )N
   r   r   r   r   r   _reset_expiresr$   $   s    R  r   c                :    t        |       dk  rt        dd      y )N     z&Password must be at least 8 charactersstatus_codedetail)lenr   )passwords    r   _validate_password_complexityr-   )   s#     8}q4\]] r   c                   t         j                  }t         j                  }t         j                  }t         j                  }t         j
                  }|r|r|st        d      t               }d|d<   ||d<   | |d<   |j                  dj                  |             t        j                  ||      5 }|j                          |j                          |j                          |r@|r>|j                  |j                         |j!                  dd	      j                                |j#                  |       d d d        y # 1 sw Y   y xY w)
NzSMTP configuration incompletezPassword Reset LinkSubjectFromTozA request was received to reset your password.

Reset link:
{link}

This link will expire in 10 minutes. If you did not request this, you can ignore this email.
)link  )r   	SMTP_HOST	SMTP_PORT	SMTP_USER	SMTP_PASS	SMTP_FROMRuntimeErrorr   set_contentformatsmtplibSMTPehlostarttlsloginstripreplacesend_message)		recipient
reset_linkhostport	smtp_user	smtp_passsendermsgsmtps	            r   _send_reset_emailrN   0   s   DD""I""IFt6:;;
.C*C	NCKCIOO	 F
F 
dD	! T				JJy()*;*;C*D*J*J*LM#  s   2BD??Ec                 K   ||k7  rt        dd      | j                  t        t              j	                  t        j
                  |k(               d {   }|j                         rt        dd      t        |t        |            }| j                  |       | j                          d {    | j                  |       d {    |S 7 {7 !7 
w)Nr'   Passwords do not matchr(   i  zEmail already registered)emailpassword_hash)r   executer	   r   whererQ   scalar_one_or_noner   addcommitrefresh)dbrQ   r,   confirm_passwordexistingusers         r   signupr]   S   s     ##4LMMZZt 2 24::3F GHHH""$4NOOe=+BCDFF4L
))+
**T
K I s7   ACCAC2C3CCCCCc                L  K   t               }| j                  t        t              j	                  t        j
                  |k               d{   }| j                          d{    	 |j                  }t        j                  d|       y7 @7 *# t        $ r d}Y (w xY ww)aN  Delete tokens that are expired or have already been used.

    This is invoked on each password-reset flow invocation to keep the table
    tidy and ensure tokens older than the configured expiry (10 minutes) do
    not linger indefinitely.  We commit immediately because callers usually
    perform their own commits afterwards.
    Nz2Cleanup removed %s expired password-reset token(s))r   rS   r   r   rT   
expires_atrW   rowcountAttributeErrorloggerdebug)rY   nowresultcounts       r   _cleanup_reset_tokensrg   d   s      (C ::!"(();)F)F#)MN F ))+ LLEuM   sH   AB$BB$&B'B$,B 8B$B$B!B$ B!!B$c               r  K   |j                         }t        |        d {    | j                  t        t              j                  t        j                  |k(               d {   }|j                         }|r|j                  st        j                  d|       yt        j                  d      }t        |      }t        |j                  |t!               t#               z   d       }| j%                  |       | j'                          d {    t(        j*                  xs dj-                  d      }| d| }	 t/        ||       t        j                  d	|       d|fS 7 G7 7 \# t0        $ r;}	t        j3                  d
||	       t        j                  d||       Y d }	~	d|fS d }	~	ww xY ww)Nz8Password reset requested for email=%s (generic response))FN0   )user_id
token_hashr_   used_atr4   /z/reset-password?token=)rE   rF   z#Password reset email sent. email=%sz6Failed to send password reset email. email=%s error=%sz4Password reset link (fallback log). email=%s link=%sT)rB   rg   rS   r	   r   rT   rQ   rU   	is_activerb   infosecretstoken_urlsafer   r   idr   r$   rV   rW   r   FRONTEND_URLrstriprN   	Exceptionwarning)
rY   rQ   re   r\   	raw_tokenrk   prtfrontendrF   excs
             r   forgot_passwordr{   {   sz    KKME

###::fTl00u1DEFFF$$&D t~~NPUV%%b)I#I.J
8n..	C FF3K
))+%%+33C8H:3I;?J_EjA95A ?E $F*   _OQVX[\JES]^^?_s_   F7E(AF7'E+(B)F7E..F7#E0 $F7+F7.F70	F49.F/'F7/F44F7c               J  K   t        |        d {    ||k7  rt        dd      t        |       t        |      }| j	                  t        t              j                  t        j                  |k(               d {   }|j                         }|st        dd      t               }|j                  |k  rt        dd      |j                  t        dd      | j	                  t        t              j                  t        j                  |j                  k(               d {   }|j                         }	|	r|	j                   st        dd      t#        ||	j$                        rt        dd      t'        |      |	_        ||_        | j	                  t)        t*              j                  t*        j                  |	j                  k(        j-                  d	             d {    ||	_        | j1                          d {    y 7 7 7 7 ,7 w)
Nr'   rP   r(   zInvalid or Expired LinkzReset link has expiredzThis link has already been usedz1New password must not match the previous passwordTrevoked)rg   r   r-   r   rS   r	   r   rT   rk   rU   r   r_   rl   r   rr   rj   rn   r   rR   r   r
   r   valuestokens_invalid_beforerW   )
rY   tokennew_passwordrZ   rk   re   storedrd   user_resultr\   s
             r   reset_passwordr      s    

###''4LMM!,/#E*J::f%78>>?Q?\?\`j?jkllF&&(F4MNN
(CC4LMM~~!4UVV

6$<#5#5dgg6O#PQQK))+Dt~~4MNN|T%7%784ghh&|4DFN
**VL)//0D0D0OPWW`dWe
fff!$D
))+
G $ m R g sZ   H#HA-H#?H B-H#-H.CH#2H3H#H!H#H#H#H#!H#c                 K   | j                  t        t              j                  t        j                  |k(               d {   }|j                         }|r|j                  st        t        j                  d      t        ||j                        st        t        j                  d      t        t        |j                        |j                  dt                     }t        t        |j                        |j                  dt!                     }t#        |j                  t%        |      t'               t!               z   d      }| j)                  |       | j+                          d {    ||fS 7 97 w)NzInvalid credentialsr(   accesssubrQ   
token_typeexpires_deltarX   Frj   rk   r_   r~   )rS   r	   r   rT   rQ   rU   rn   r   r   HTTP_401_UNAUTHORIZEDr   rR   r   strrr   r   r!   r   r   r   rV   rW   )rY   rQ   r,   re   r\   access_tokenrefresh_tokenrts           r   rA   rA      s    ::fTl00u1DEFFF$$&Dt~~(D(DMbcc8T%7%78(D(DMbccCL

xgvgxyL S\PYiyi{|M	%m48.00	
B FF2J
))+&&) G$ s%   AFE=D/F4F 5	F Fc                 K   ddl m} 	  ||      }|j                  d      dk7  rt        t        j
                  d      t        |      }| j                  t        t              j                  t        j                  |k(               d {   }|j                         }|r#|j                  s|j                  t!               k  rt        t        j
                  d      | j                  t        t"              j                  t"        j$                  |j&                  k(               d {   }|j                         }|r|j(                  st        t        j
                  d      d|_        t+        t-        |j$                        |j.                  dt1               	      }	t+        t-        |j$                        |j.                  dt3               	      }
t        |j$                  t        |
      t!               t3               z   d
      }| j5                  |       | j7                          d {    |	|
fS # t        $ r t        t        j
                  d      w xY w7 7 :7 6w)Nr   )decode_tokenzInvalid tokenr(   typerX   Tr   r   Fr   )app.core.securityr   
ValueErrorr   r   r   getr   rS   r	   r   rT   rk   rU   r~   r_   r   r   rr   rj   rn   r   r   rQ   r   r!   rV   rW   )rY   r   r   payloadrk   re   r   r   r\   
new_accessnew_refresh
new_storeds               r   rX   rX      s    .^}- {{6i'(D(D_]]#M2J::|""<#:#:j#HI F &&(FV^^v'8'8FH'D(D(D_]]

6$<#5#5dgg6O#PQQK))+Dt~~(D(D_]]FN#dgg,djjXetevwJ3tww<tzzigwgyzK%k28.00	J FF:
))+{""I  ^(D(D_]]^ R$ sN   I,H< A;I,I$BI,-I'.DI,3I*4I,<%I!!I,'I,*I,c                 K   t        |      }| j                  t        t              j	                  t        j
                  |k(               d {   }|j                         }|sy | j                  t        t              j	                  t        j                  |j                  k(        j                  d             d {    | j                  t        t              j	                  t        j                  |j                  k(        j                  t                            d {    | j                          d {    y 7 7 7 !7 w)NTr}   )r   )r   rS   r	   r   rT   rk   rU   r
   rj   r   r   rr   r   rW   )rY   r   rk   re   r   s        r   logoutr     s    #M2J::f\2889P9PT^9^_``F&&(F
**|	|##v~~5	6		  
 **t	tww&..(	)	fh	/  
 ))+ a


 sJ   AEEA2EE
A'E*E+EEE
EEE)returnr   )r,   r   r   None)rE   r   rF   r   r   r   )
rY   r   rQ   r   r,   r   rZ   r   r   r   )rY   r   r   r   )rY   r   rQ   r   r   ztuple[bool, str | None])
rY   r   r   r   r   r   rZ   r   r   r   )rY   r   rQ   r   r,   r   r   tuple[str, str])rY   r   r   r   r   r   )rY   r   r   r   r   r   )/
__future__r   loggingrp   r=   datetimer   email.messager   fastapir   r   
sqlalchemyr	   r
   r   sqlalchemy.ext.asyncior   app.core.configr   r   r   r   r   r   r   app.models.refresh_tokenr   app.models.password_reset_tokenr   app.models.userr   	getLogger__name__rb   r   r!   r$   r-   rN   r]   rg   r{   r   rA   rX   r   r   r   r   <module>r      s    "     & ) - - / $  2 >  			8	$C>!
^ F"N.%P$N'0)#Xr   