
    i6                     \   U d Z ddlZddlZddlmZ ddlZddlmZmZm	Z	m
Z
 ddlmZmZmZ ddlmZmZmZ ddlmZ  ej*                  e      Z e       Zi Zeed<   d	Zd
efdZdedee   d
ee   fdZ  ee      fded
efdZ! ed      Z" e	e"      fded
efdZ# ee!      fded
efdZ$y)u   
Autenticación JWT via Keycloak.

Valida tokens Bearer emitidos por Keycloak usando JWKS (RS256).
Las claves públicas se cachean 1 hora para no consultar Keycloak en cada request.
    N)Optional)DependsHTTPExceptionSecuritystatus)
HTTPBearerHTTPAuthorizationCredentialsAPIKeyHeader)jwtJWTErrorExpiredSignatureError)settings_jwks_cachei  returnc                    K   t        j                          } t        j                  d      r)t        j                  dd      t        z   | kD  r	t        d   S t        j
                   d}t        j                  d|        t        j                         4 d{   }|j                  |d       d{   }|j                          |j                         }ddd      d{    d   t        d<   | t        d<   t        j                  d	t        |d          d
       t        d   S 7 7 y7 K# 1 d{  7  sw Y   [xY ww)u>   Obtiene las claves públicas de Keycloak con caché de 1 hora.keys	cached_atr   z/protocol/openid-connect/certsu'   🔑 Actualizando JWKS desde Keycloak: N
   )timeoutu   ✅ JWKS cacheadas: z	 clave(s))timer   get_JWKS_CACHE_TTLr   KEYCLOAK_ISSUER_URIloggerinfohttpxAsyncClientraise_for_statusjsonlen)nowjwks_urlclientresponsedatas        (/opt/lhia/marcimex/agent/app/api/auth.py	_get_jwksr'      s    
))+CvKOOK$Co$UX[$[6""..//MNH
KK9(DE  "  fHb99!!#}} 
 v,K"K
KK&s4<'8&9CDv9   s[   BED+ED13D-4$D1E#D/$AE-D1/E1E7D:8E?Er   kidc                 D    rt        fd| D        d      S | r| d   S dS )z:Busca la clave por kid. Si no hay kid, retorna la primera.c              3   L   K   | ]  }|j                  d       k(  s|  yw)r(   Nr   ).0kr(   s     r&   	<genexpr>z_find_key.<locals>.<genexpr>2   s      <1e(;Q<s   $$Nr   )next)r   r(   s    `r&   	_find_keyr0   /   s*    
<<dCC47$$    credentialsc                   K   | j                   }t        t        j                  dddi      }	 t	        j
                  |      }|j                  d      }t                d{   }t        ||      }|s2t        j                          t                d{   }t        ||      }|st        j                  d| d       |t	        j                  ||d	gt        j                  d
di      }t        j!                  d|j                  d       d|j                  d              |S 7 7 # t"        $ r4 t        j                  d       t        t        j                  dddi      t$        $ r}t        j                  d|        |d}~wt&        j(                  $ r8}t        j+                  d|        t        t        j,                  d      d}~ww xY ww)u  
    Dependencia FastAPI: valida el Bearer JWT emitido por Keycloak.
    Retorna el payload (claims) del token si es válido.

    Uso en rutas:
        @router.get("/ruta")
        async def mi_ruta(payload: dict = Depends(verify_token)):
            ...

    Uso en include_router (protege todo el router):
        app.include_router(router, dependencies=[Depends(verify_token)])
    u   Token inválido o no autorizadozWWW-AuthenticateBearer)status_codedetailheadersr(   Nu   ⚠️ Clave kid='z#' no encontrada en JWKS de KeycloakRS256
verify_audF)
algorithmsissueroptionsu   ✅ Token válido | user=preferred_usernamez
 | client=azpu   ⚠️ Token JWT expiradozToken expiradou   ⚠️ Error validando JWT: u(   ❌ Error de conectividad con Keycloak: z=No se pudo verificar el token (error conectando con Keycloak)r5   r6   )r2   r   r   HTTP_401_UNAUTHORIZEDr   get_unverified_headerr   r'   r0   r   clearr   warningdecoder   r   debugr   r   r   	HTTPErrorerrorHTTP_503_SERVICE_UNAVAILABLE)	r2   tokenunauthorizedheaderr(   r   keypayloades	            r&   verify_tokenrO   6   s     ##E 000#X.L+
**51jj[ c""$DD#&CNN/u4WXY**y//!5)
 	0=Q1R0SS]^e^i^ijo^p]qrs- ! %$ ! 
2344#'2
 	

  5aS9:?? 
?sCD;;R
 	

sZ   +G3D- !D)"3D- D+BD- (G)D- +D- -AG1FG!3GGGz	X-API-Key)nameapi_keyc                    K   t         j                  r| t         j                  k7  r0t        j                  d       t	        t
        j                  d      | S w)z
    Dependencia FastAPI: valida la API Key enviada en el header X-API-Key.
    Configurada mediante la variable de entorno SYNC_API_KEY.
    u.   ⚠️ Intento de acceso con API Key inválidau"   API Key inválida o no configuradar?   )r   SYNC_API_KEYr   rC   r   r   HTTP_403_FORBIDDEN)rQ   s    r&   verify_api_keyrU   }   sK     
   Gx/D/D$DGH117
 	
 Ns   AArM   c                 L    | j                  d      xs | j                  dd      S )z
    Dependencia de conveniencia: retorna el preferred_username del token.

    Uso:
        @router.get("/ruta")
        async def mi_ruta(username: str = Depends(get_current_user)):
            ...
    r=   subunknownr+   )rM   s    r&   get_current_userrY      s$     ;;+,ME90MMr1   )%__doc__r   loggingtypingr   r   fastapir   r   r   r   fastapi.securityr   r	   r
   joser   r   r   config.settingsr   	getLogger__name__r   securityr   dict__annotations__r   listr'   strr0   rO   _api_key_headerrU   rY    r1   r&   <module>rj      s        < < S S 5 5 $			8	$< T  (%D %x} %$ % 180AA
-A
	A
H K0 )1(A # c  &-\%: 	Nd 	Ns 	Nr1   