
    ٞi0                         d Z ddlZddlZddlZddlZddlZddlmZmZm	Z	 ddl
m
Z
mZ ddlmZ ddlmZ  ej                   e      Z G d d      Z G d	 d
      Z G d d      Z G d d      ZdefdZy)zV
Security Module
Authentication, authorization, rate limiting, and input sanitization
    N)OptionalDictTuple)datetime	timedelta)defaultdictwrapsc                   8    e Zd ZdZdefdZdefdZdedefdZy)	TokenManageru%   Gestionnaire de tokens API sécurisé
script_dirc                 x    || _         t        j                  j                  |d      | _        d | _        d | _        y )Nz
.api_token)r   ospathjoin
token_file_token_token_hash)selfr   s     //home/ubuntu/crypto_trading_bot/api/security.py__init__zTokenManager.__init__   s.    $'',,z<@%)*.    returnc                    | j                   r| j                   S t        j                  j                  d      }|sgt        j                  j                  | j                        r>	 t        | j                  d      5 }|j                         j                         }ddd       |st        j                  d      }	 t        | j                  d      5 }|j                  |       ddd       t        j                   dk7  r t        j"                  | j                  d       t        j%                  d	|dd
  d       || _         t'        j(                  |j+                               j-                         | _        |S # 1 sw Y   xY w# t        $ r#}t        j                  d|        Y d}~	d}~ww xY w# 1 sw Y   xY w# t        $ r"}t        j                  d|        Y d}~d}~ww xY w)u%   Récupérer ou générer le token APIDASHBOARD_API_TOKENrNzErreur lecture token:     wnt  u   Nouveau token généré:    ...zErreur sauvegarde token: )r   r   environgetr   existsr   openreadstrip	Exceptionloggererrorsecretstoken_urlsafewritenamechmodwarninghashlibsha256encode	hexdigestr   )r   tokenfes       r   	get_tokenzTokenManager.get_token   s}   ;;;; 

45 8;$//3/ -1FFHNN,E- ))"-E	>$//3/ #1GGEN# 77d?HHT__e4!:5!9+SIJ ">>%,,.9CCE+- - ;5aS9::;# #  >8<==>sa   $F :E7F 9F> F2!AF> 7F <F 	F/F**F/2F;7F> >	G)G$$G)provided_tokenc                 N    | j                         }t        j                  ||      S )u0   Vérifier un token avec protection timing attack)r9   r,   compare_digest)r   r:   expecteds      r   verify_tokenzTokenManager.verify_token>   s!    >>#%%nh??r   N)	__name__
__module____qualname____doc__strr   r9   boolr>    r   r   r   r      s4    //3 / 3  D@3 @4 @r   r   c                   N    e Zd ZdZd
dedefdZdedeee	e   f   fdZ
defdZy	)RateLimiteru    Rate limiter simple basé sur IPmax_requestswindow_secondsc                 \    || _         t        |      | _        t        t              | _        y )N)seconds)rH   r   windowr   listrequests)r   rH   rI   s      r   r   zRateLimiter.__init__G   s"    (7)4T):r   	client_ipr   c                    t        j                         }|| j                  z
  }| j                  |   D cg c]	  }||kD  r| c}| j                  |<   t	        | j                  |         | j
                  k\  rOt        | j                  |         }t        || j                  z   |z
  j                               }dt        d|      fS | j                  |   j                  |       yc c}w )uj   
        Vérifier si une requête est autorisée
        Returns: (allowed, retry_after_seconds)
        F   TN)r   nowrL   rN   lenrH   mininttotal_secondsmaxappend)r   rO   rS   cutoffreq_timeoldestretry_afters          r   
is_allowedzRateLimiter.is_allowedL   s    
 lln t{{"%)]]9%=$
!&  $
i  t}}Y'(D,=,==y12Fv3c9HHJKK#a--- 	i '',$
s   C&c                 <    || j                   v r| j                   |= yy)u&   Réinitialiser le compteur pour une IPN)rN   )r   rO   s     r   resetzRateLimiter.resetd   s    %i( &r   N)d   <   )r?   r@   rA   rB   rV   r   rC   r   rD   r   r^   r`   rE   r   r   rG   rG   D   sE    *;S ; ;
C E$2E,F 0)s )r   rG   c                   ^    e Zd ZdZededefd       Zededefd       Zededefd       Z	y	)
SecurityValidatoru(   Validateur de sécurité pour les inputsfilenamer   c                 Z    | syg d}|D ]  }|| v s y ddl }|j                  d|       syy)u'   Vérifier qu'un nom de fichier est sûrF)z../\ 
r   Nz^[a-zA-Z0-9_\-\.]+$T)rematch)re   	dangerouscharrl   s       r   is_safe_filenamez"SecurityValidator.is_safe_filenamem   sE      8	 	Dx	
 	xx.9r   symbolc                 b    | rt        |       dkD  ryddl}t        |j                  d|             S )u)   Vérifier qu'un symbole crypto est valide   Fr   Nz^[A-Z0-9]+USDT$)rT   rl   rD   rm   )rq   rl   s     r   is_safe_symbolz SecurityValidator.is_safe_symbol   s/     Vr)BHH/899r   messagec                     | sy| j                  dd      j                  dd      j                  dd      }t        |      dkD  r|dd d	z   }|S )
u1   Nettoyer un message de log pour éviter injection rj    rk   	i  Ni  r"   )replacerT   )ru   	sanitizeds     r   sanitize_log_messagez&SecurityValidator.sanitize_log_message   s[      OOD#.66tSAII$PST	 y>C!$3%/Ir   N)
r?   r@   rA   rB   staticmethodrC   rD   rp   rt   r|   rE   r   r   rd   rd   j   si    23 4  $ :s :t : : c c  r   rd   c                       e Zd ZdZh dZdedefdZdededefd	Z	d
 Z
ddedee   defdZdedefdZddedededeeee   f   fdZdedeeee   ee   f   fdZy)AuthMiddlewareuA   Middleware d'authentification — HTTP Basic Auth (user/password)>	   /api/health/favicon.ico/mobile.html/manifest.json/api/crypto-news/api/mobile-summary/manifest-mobile.json/crypto_trading_ia_icon.png/crypto_trading_ia_logo.pngtoken_managerrate_limiterc                     || _         || _        d | _        t        j                  j                  |j                  d      | _        | j                          y )Nz.dashboard_auth)	r   r   _credentialsr   r   r   r   
_cred_file_load_or_create_credentials)r   r   r   s      r   r   zAuthMiddleware.__init__   sC    *(7;'',,}'?'?ARS((*r   passwordsaltr   c                 n    t        j                  | d| j                               j                         S )z%Hash le mot de passe avec sel SHA-256:)r2   r3   r4   r5   )r   r   r   s      r   _hash_passwordzAuthMiddleware._hash_password   s.    ~~az299;<FFHHr   c                 p   t         j                  j                  d      }t         j                  j                  d      }|r8|r6d}|| j                  ||      |f| _        t
        j                  d| d       yt         j                  j                  | j                        r	 t        | j                  d      5 }|j                         j                         j                  d      }ddd       t              d	k(  r*|\  }}}|||f| _        t
        j                  d
| d       y	 d}t#        j$                  d      }	t#        j&                  d      }| j                  |	|      }|||f| _        	 t        | j                  d      5 }|j)                  | d| d|        ddd       t         j*                  dk7  r t        j,                  | j                  d       t
        j/                  d       t
        j/                  d       t
        j/                  d|        t
        j/                  d|	        t
        j/                  d| j                          t
        j/                  d       t
        j/                  d       t1        d       t1        d       t1        d|        t1        d|	        t1        d       y# 1 sw Y   xY w# t        $ r#}t
        j!                  d|        Y d}~d}~ww xY w# 1 sw Y   ]xY w# t        $ r#}t
        j!                  d|        Y d}~Td}~ww xY w)u/   Charge ou génère le couple login/mot de passeDASHBOARD_USERDASHBOARD_PASSWORDenvu>   🔐 Auth: credentials depuis variables d'environnement (user=)Nr   r      u&   🔐 Auth: credentials chargés (user=zErreur lecture credentials: admin   r!   r   r   r    zErreur sauvegarde credentials: z<============================================================u.   🔐 NOUVEAUX CREDENTIALS DASHBOARD GÉNÉRÉSz   Utilisateur : z   Mot de passe: z   Fichier     : u,      Conservez ces informations en lieu sûr !z=
============================================================u2   🔐 CREDENTIALS DASHBOARD (première utilisation)z=============================================================
)r   r#   r$   r   r   r*   infor   r%   r   r&   r'   r(   splitrT   r)   r+   r,   r-   	token_hexr.   r/   r0   r1   print)
r   env_userenv_passr   r7   partsusernamepassword_hashr8   r   s
             r   r   z*AuthMiddleware._load_or_create_credentials   s    ::>>"23::>>"67D!)4+>+>x+NPT UDKKXYaXbbcde 77>>$//*	A$//3/ 81FFHNN,2237E8u:?491HdM)1=$(GD%KK"H
RS TU	 # ((,  #++Hd;%}d;	@doos+ >q8*AdV1]O<=>ww$%0 	x GH*8*56*8*56*4??*;<=EFx oBC!(,-!(,-oI8 8  A;A3?@@A> >  	@LL:1#>??	@s`   #K 9.K '?K 1L	 K<!;L	  K
K 	K9K44K9<LL	 		L5L00L5Nnew_passwordnew_usernamec                 $   	 |xs | j                   r| j                   d   nd}t        j                  d      }| j                  ||      }|||f| _         t	        | j
                  d      5 }|j                  | d| d|        ddd       t        j                  dk7  r t        j                  | j
                  d       t        j                  d	|        y
# 1 sw Y   UxY w# t        $ r"}t        j                  d|        Y d}~yd}~ww xY w)z5Changer le mot de passe (et optionnellement le login)r   r   r!   r   r   Nr   r    u-   🔐 Mot de passe changé pour l'utilisateur Tz Erreur changement mot de passe: F)r   r,   r   r   r&   r   r.   r   r/   r0   r*   r   r)   r+   )r   r   r   r   r   r   r7   r8   s           r   change_passwordzAuthMiddleware.change_password   s    	#_@Q@Q(9(9!(<W^H$$Q'D //dCM!)=$ ?Ddoos+ >q8*AdV1]O<=>ww$%0KKGzRS> >  	LL;A3?@	s1   A(C$ *CAC$ C!C$ $	D-D

Dauth_headerc                    |j                  d      sy	 t        j                  |dd       j                  d      }d|vry|j	                  dd      \  }}| j                  sy| j                  \  }}}t        j                  ||      }| j                  ||      }	t        j                  |	|      }
|xr |
S # t
        $ r Y yw xY w)u;   Vérifie un header Authorization: Basic <base64(user:pass)>Basic F   Nzutf-8r   rQ   )

startswithbase64	b64decodedecoder   r)   r   r,   r<   r   )r   r   decodedprovided_userprovided_passr   r   r   user_ok	pass_hashpass_oks              r   verify_basic_authz AuthMiddleware.verify_basic_auth   s    %%h/	&&{127>>wGG'!+2==a+@(M=   (,(9(9%- ((A''t<	((MB"7"  		s   +B6  B6 6	CCheadersrO   allow_localc                 D   |r|dv ry|j                  dd      }|j                  d      r+| j                  |      ryt        j	                  d|        y|j                  d      r:|d	d
 }| j
                  j                  |      ryt        j	                  d|        yy)ul   
        Vérifier l'authentification HTTP Basic Auth.
        Returns: (authorized, error_message)
        )z	127.0.0.1z::1	localhostrR   Authorizationrw   r   u#   Échec authentication Basic depuis )FzIdentifiants incorrectszBearer    NzToken Bearer invalide depuis )FzToken invalide)FzAuthentification requise)r$   r   r   r*   r1   r   r>   )r   r   rO   r   r   r6   s         r   
check_authzAuthMiddleware.check_auth  s     9(IIkk/26 !!(+%%k2!NN@LM3 !!),OE!!..u5!NN:9+FG*0r   c                 V    | j                   j                  |      \  }}|s	dd| d|fS y)u`   
        Vérifier le rate limit
        Returns: (allowed, error_message, retry_after)
        Fz!Rate limit exceeded. Retry after s)TNN)r   r^   )r   rO   allowedr]   s       r   check_rate_limitzAuthMiddleware.check_rate_limit*  s<    
  $00;;IF=k]!LkYYr   )N)T)r?   r@   rA   rB   PUBLIC_PATHSr   rG   r   rC   r   r   r   rD   r   r   dictr   r   rV   r   rE   r   r   r   r      s    KPL+l ++ +Is I# I# I2hC x} X\ "#S #T #01$ 13 1T 1UZ[_aijman[nUo 18
 # 
 %hsmXVY]8Z2[ 
 r   r   r   c                     d }|S )u*   Décorateur pour exiger l'authentificationc                 .     t                fd       }|S )Nc                     | j                   d   }| j                         s| j                  ddid       y  | g|i |S )Nr   r+   Unauthorizedi  )client_addressr   send_json_response)r   argskwargsrO   handler_methods       r   wrapperz0require_auth.<locals>.decorator.<locals>.wrapper:  sL    ++A.I ??$''.(A3G!$8888r   r	   )r   r   s   ` r   	decoratorzrequire_auth.<locals>.decorator9  s!    	~		9 
	9 r   rE   )r   r   s     r   require_authr   7  s     r   )rB   r   r   r2   r,   loggingtypingr   r   r   r   r   collectionsr   	functoolsr
   	getLoggerr?   r*   r   rG   rd   r   r   rE   r   r   <module>r      sx   
 
     ( ( ( # 			8	$.@ .@b#) #)L, ,^[  [ | r   