
    fai'                     X    d Z ddlZddlZddlmZmZ ddlmZmZmZ  G d d          Z	dS )u   
app/services/auth_service.py
============================
Servizio autenticazione con JWT token.

Responsabilità:
- Recuperare user da database
- Validare password
- Generare JWT token
- Decodificare e validare JWT
- Gestire refresh token
    N)datetime	timedelta)DictOptionalTuplec                   0   e Zd ZdZ	 ddedededefdZd	 Zddededede	e
e         e
e         e
e         f         fdZdefdZdede	e
e         e
e         f         fdZdede	e
e         e
e         f         fdZ	 	 	 ddededede	eef         fdZdS )AuthServiceu\  
    Servizio centralizzato per autenticazione JWT.
    
    Parametri iniziali:
        db_config (dict): Credenziali MySQL {host, user, password, database}
        jwt_secret (str): Secret key per firmare JWT
        jwt_algorithm (str): Algoritmo JWT (default: HS256)
        jwt_expiration_hours (int): Ore di validità token (default: 24)
    HS256   	db_config
jwt_secretjwt_algorithmjwt_expiration_hoursc                 >    || _         || _        || _        || _        d S )N)r   r   r   r   )selfr   r   r   r   s        F/var/www/enigma.pooltech.it/enigma_inventario/services_auth_service.py__init__zAuthService.__init__   s&    "$*$8!!!    c                    	 t          j        | j        d         | j        d         | j        d         | j        d         dt           j        j                  }|S # t           j        $ r$}t          dt          |                     d}~ww xY w)	z
        Crea connessione MySQL.
        
        Returns:
            pymysql.Connection: Connessione al database
            
        Raises:
            Exception: Se connessione fallisce
        hostuserpassworddatabaseutf8mb4)r   r   r   r   charsetcursorclasszDatabase connection error: N)pymysqlconnectr   cursors
DictCursorError	Exceptionstr)r   connes      r   _get_db_connectionzAuthService._get_db_connection&   s    	D?^F+^F+
3
3!#O6  D K} 	D 	D 	DB#a&&BBCCC	Ds   AA B(BB   emailr   	tenant_idreturnc                    d}	 |                                  }|                                5 }d}|                    |||f           |                                }ddd           n# 1 swxY w Y   |s"ddd| d| f|r|                                 S S ddlm} |                    |          }	|	                    |          s	 |r|                                 dS dS | 	                    |	          }
|                                5 }d}|                    |t          j                    |	j        f           |                                 ddd           n# 1 swxY w Y   |
|	                                df|r|                                 S S # t          $ r6}ddd	t!          |           fcY d}~|r|                                 S S d}~ww xY w# |r|                                 w w xY w)
uU  
        Login user e genera JWT token.
        
        Per la DEMO: tenant_id=1 è sempre il tenant di test.
        
        Args:
            email (str): Email utente
            password (str): Password in chiaro
            tenant_id (int): ID tenant (default: 1 per demo)
            
        Returns:
            Tuple[str, dict, str]: (access_token, user_dict, error_message)
                - success: (token, user_data, None)
                - failure: (None, None, error_message)
                
        Esempio:
            >>> service = AuthService(db_config, jwt_secret='secret123')
            >>> token, user, error = service.login('operatore@studium.it', 'password123')
            >>> if token:
            ...     print(f"Login OK: {user['nome']}")
            ... else:
            ...     print(f"Login failed: {error}")
        Nz
                    SELECT id, id_tenant, email, nome, password_hash, ruolo
                    FROM tenant_users
                    WHERE email = %s AND id_tenant = %s
                    LIMIT 1
                User z not found in tenant r   User)NNzInvalid passwordz9UPDATE tenant_users SET ultimo_accesso = %s WHERE id = %szLogin error: )r&   cursorexecutefetchoneclosemodels_userr.   	from_dictverify_passwordgenerate_tokenr   utcnowidcommitto_dictr"   r#   )r   r(   r   r)   r$   r/   sqluser_rowr.   r   tokenr%   s               r   loginzAuthService.login=   s   0 )	**,,D  -& sUI$6777!??,,- - - - - - - - - - - - - - -  ST#R5#R#Ry#R#RR.  

) )(((((>>(++D''11 65   

  ''--E  &QsX_%6%6$@AAA              
 $,,..$.
  

  	8 	8 	87s1vv77777777 

	8  

s   (F /A'F 'A++F .A+/F 0F !)F 
AEF EF "E#F 
GG2G3G GG G0c                     |                                 }t          j                    t          | j                  z   |d<   t          j                    |d<   d|d<   t          j        || j        | j                  }|S )at  
        Genera JWT token per user.
        
        Args:
            user: Istanza User (da models.user)
            
        Returns:
            str: JWT token firmato
            
        Nota: Il token contiene:
        - user_id, email, tenant_id, ruolo (dati critici per autorizzazione)
        - exp: timestamp scadenza
        - iat: timestamp creazione
        hoursexpiatBearer
token_type	algorithm)	to_jwt_payloadr   r7   r   r   jwtencoder   r   )r   r   payloadr=   s       r   r6   zAuthService.generate_token   sx     %%'' "**YT=V-W-W-WW!** ( 
7DOt?QRRRr   r=   c                    	 t          j        || j        | j        g          }|dfS # t           j        $ r Y dS t           j        $ r}ddt          |           fcY d}~S d}~wt          $ r}ddt          |           fcY d}~S d}~ww xY w)aP  
        Valida JWT token e restituisce payload.
        
        Args:
            token (str): JWT token (senza "Bearer " prefix)
            
        Returns:
            Tuple[dict, str]: (payload, error_message)
                - valid: (payload_dict, None)
                - invalid: (None, error_message)
                
        Esempio:
            >>> payload, error = service.validate_token(token)
            >>> if payload:
            ...     print(f"Token valid for user {payload['user_id']}")
            ... else:
            ...     print(f"Token invalid: {error}")
        )
algorithmsN)NzToken expiredzInvalid token: zToken validation error: )rI   decoder   r   ExpiredSignatureErrorInvalidTokenErrorr#   r"   )r   r=   rK   r%   s       r   validate_tokenzAuthService.validate_token   s    &	=jTEWDXYYYGD= ( 	) 	) 	)((($ 	4 	4 	433q66333333333 	= 	= 	=<CFF<<<<<<<<<	=s2   %( BBA!B!B.BBB	old_tokenc                    	 t          j        || j        | j        gddi          }n0# t           j        $ r}ddt          |           fcY d}~S d}~ww xY wd |                                D             }t          j                    t          | j
                  z   |d<   t          j                    |d	<   	 t          j        || j        | j        
          }|dfS # t          $ r}ddt          |           fcY d}~S d}~ww xY w)u  
        Refresh JWT token (genera nuovo token).
        
        Args:
            old_token (str): Token attuale (anche se scaduto, validiamo comunque)
            
        Returns:
            Tuple[str, str]: (new_token, error_message)
                
        Nota: Questo permette al client di ottenere un nuovo token
              senza re-autenticarsi se il vecchio è scaduto.
        
verify_expF)rM   optionsNzCannot refresh invalid token: c                 "    i | ]\  }}|d v	||S ))rB   rC    ).0kvs      r   
<dictcomp>z-AuthService.refresh_token.<locals>.<dictcomp>   s(    SSS11N;R;Rq!;R;R;Rr   r@   rB   rC   rF   zToken refresh error: )rI   rN   r   r   rP   r#   itemsr   r7   r   r   rJ   r"   )r   rR   rK   r%   new_payload	new_tokens         r   refresh_tokenzAuthService.refresh_token   sO   	CjDO,0,>+?)5u(=? ? ?GG $ 	C 	C 	CB#a&&BBBBBBBBB	C TSSSS%_..AZ1[1[1[[E%_..E	:
;4K]^^^Id?" 	: 	: 	:9Q999999999	:s8   %( AA
AA9$C 
D(D;DDdemo@studium.itdemo123c           	         ddl m} d}	 |                                 }|                    |          }|                                5 }|                    d||f           |                                }|r"d}	|                    |	|||f           d| d}
n#d}	|                    |	||d	|d
f           d| d}
|                                 d|
fcddd           |r|                                 S S # 1 swxY w Y   nB# t          $ r5}ddt          |           fcY d}~|r|                                 S S d}~ww xY w	 |r|                                 dS dS # |r|                                 w w xY w)a  
        Crea user di test nel database per la DEMO.
        
        SOLO per ambiente di demo! In produzione non usare mai.
        
        Args:
            email (str): Email test user
            password (str): Password test user
            tenant_id (int): Tenant ID (default 1)
            
        Returns:
            Tuple[bool, str]: (success, message)
            
        Nota: Se user esiste, lo aggiorna. Se non esiste, lo crea.
        r   r-   Nz?SELECT id FROM tenant_users WHERE email = %s AND id_tenant = %sz
                        UPDATE tenant_users 
                        SET password_hash = %s, data_creazione = NOW()
                        WHERE email = %s AND id_tenant = %s
                    r,   z updatedz
                        INSERT INTO tenant_users 
                        (id_tenant, email, nome, password_hash, ruolo, data_creazione)
                        VALUES (%s, %s, %s, %s, %s, NOW())
                    z	Demo User	operatorez createdTFzError creating test user: )r3   r.   r&   hash_passwordr/   r0   r1   r9   r2   r"   r#   )r   r(   r   r)   r.   r$   pwd_hashr/   existingr;   messager%   s               r   create_test_userzAuthService.create_test_user   sH   $ 	%$$$$$,	**,,D))(33H "%&UI&   "??,, 6C
 NN35)(DEEE5e555GGC
 NN3!# #)    6e555GW}E"% "% "% "% "% "% "%N  

O"% "% "% "% "% "% "% "% "%H  	@ 	@ 	@?s1vv???????? 

	@I"%N  

 t 

sZ   =D BC6D 6C::D =C:>D E 
ED<E E <EE E8N)r
   r   )r'   )r`   ra   r'   )__name__
__module____qualname____doc__r   r#   intr   r&   r   r   r>   r6   rQ   r_   boolrh   rW   r   r   r	   r	      s         LN9 9$ 9C 9 #9EH9 9 9 9D D D.B B3 B# B# BeHUXM[cdh[ikstwkxLxFy B B B BHc    6=C =E(4.(3-2O,P = = = =::s :uXc]HSM5Q/R : : : :@ ->(1)*A Ac A"%A#&A/4T3Y/?A A A A A Ar   r	   )
rl   rI   r   r   r   typingr   r   r   r	   rW   r   r   <module>rp      s     


  ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( (F F F F F F F F F Fr   