
    g+                         d Z ddlZddl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 ddlmZ ddlmZ dd	lmZ dd
lmZmZmZ ddlmZ  G d d      Zy)z
Supports using JWT's for authenticating into the proxy. 

Currently only supports admin. 

JWT token must have 'litellm_proxy_admin' in scope. 
    N)ListOptionalcast)x509)default_backend)serialization)verbose_proxy_logger)	DualCache)HTTPHandler)JWKKeyValue
JWTKeyItemLiteLLM_JWTAuth)PrismaClientc                      e Zd ZU dZee   ed<   eed<   	 	 d"dZ	 d#dee   dede	de
ddf
d	Zd
efdZdedefdZd
edee   fdZd
edee   dee   fdZdefdZdefdZd
edee   dee   fdZd$dee   defdZd
edee   dee   fdZd
edee   dee   fdZd
edee   dee   fdZd
edefdZdee   defdZdedee   dee   fdZ dedefdZ!d
edefd Z"d! Z#y)%
JWTHandlerz
    - treat the sub id passed in as the user id
    - return an error if id making request doesn't exist in proxy user table
    - track spend against the user id
    - if role="litellm_proxy_user" -> allow making calls + info. Can not edit budgets
    prisma_clientuser_api_key_cachereturnNc                 0    t               | _        d| _        y )Nr   )r   http_handlerleewayselfs    R/var/www/openai/venv/lib/python3.12/site-packages/litellm/proxy/auth/handle_jwt.py__init__zJWTHandler.__init__#   s     (M    litellm_jwtauthr   c                 <    || _         || _        || _        || _        y N)r   r   r   r   )r   r   r   r   r   s        r   update_environmentzJWTHandler.update_environment)   s#     +"4.r   tokenc                 @    |j                  d      }t        |      dk(  S )N.   )splitlen)r   r!   partss      r   is_jwtzJWTHandler.is_jwt5   s    C 5zQr   scopesc                 6    | j                   j                  |v ryy)NTF)r   admin_jwt_scope)r   r)   s     r   is_adminzJWTHandler.is_admin9   s    //69r   c                 d    | j                   j                  || j                   j                     S g S r   )r   team_ids_jwt_field)r   r!   s     r   get_team_ids_from_jwtz JWTHandler.get_team_ids_from_jwt>   s0    22>--@@AA	r   default_valuec                     	 | j                   j                  || j                   j                     }|S d }	 |S # t        $ r |}Y |S w xY wr   )r   end_user_id_jwt_fieldKeyErrorr   r!   r0   user_ids       r   get_end_user_idzJWTHandler.get_end_user_idC   s_    	$##99E 4 4 J JK
     	$#G	$   /8 8 AAc                 2    | j                   j                  yy)z`
        Returns:
        - True: if 'team_id_jwt_field' is set
        - False: if not
        FT)r   team_id_jwt_fieldr   s    r   is_required_team_idzJWTHandler.is_required_team_idO   s     119r   c                 z    | j                   j                  %t        | j                   j                  t              ryy)z
        Returns:
        - True: if 'user_allowed_email_domain' is set
        - False: if 'user_allowed_email_domain' is None
        TF)r   user_allowed_email_domain
isinstancestrr   s    r   is_enforced_email_domainz#JWTHandler.is_enforced_email_domainY   s7     99E*  ::CK
 r   c                     	 | j                   j                  || j                   j                     }|S | j                   j                  | j                   j                  }|S d }	 |S # t        $ r |}Y |S w xY wr   )r   r9   team_id_defaultr3   )r   r!   r0   team_ids       r   get_team_idzJWTHandler.get_team_idf   s    	$##55A 4 4 F FG  %%55A..>>
     	$#G	$s   /A& ,A& !A& &A54A5valid_user_emailc                 8    |du ry| j                   j                  S )z
        Returns:
        - True: if 'user_id_upsert' is set AND valid_user_email is not False
        - False: if not
        F)r   user_id_upsert)r   rD   s     r   is_upsert_user_idzJWTHandler.is_upsert_user_idr   s!     u$##222r   c                     	 | j                   j                  || j                   j                     }|S |}	 |S # t        $ r |}Y |S w xY wr   )r   user_id_jwt_fieldr3   r4   s       r   get_user_idzJWTHandler.get_user_id|   s_    	$##55A 4 4 F FG
  (   	$#G	$r7   c                     	 | j                   j                  || j                   j                     }|S d }	 |S # t        $ r |}Y |S w xY wr   )r   user_email_jwt_fieldr3   )r   r!   r0   
user_emails       r   get_user_emailzJWTHandler.get_user_email   sb    	'##88D"4#7#7#L#LM

  "
   	'&J	'r7   c                     	 | j                   j                  || j                   j                     }|S d }	 |S # t        $ r |}Y |S w xY wr   )r   org_id_jwt_fieldr3   )r   r!   r0   org_ids       r   
get_org_idzJWTHandler.get_org_id   s_    	###44@t33DDE
     	#"F	#r7   c                     	 t        |d   t              r|d   j                         }|S t        |d   t              r|d   }|S t	        dt        |d          d      # t        $ r g }Y |S w xY w)NscopezUnmapped scope type - z. Supported types - list, str.)r=   r>   r%   list	Exceptiontyper3   )r   r!   r)   s      r   
get_scopeszJWTHandler.get_scopes   s    	%.#.w--/  E'ND1w   ,T%.-A,BB`a   	F	s   &A A A A.-A.kidc                 B  K   t        j                  d      }|t        d      | j                  j	                  d       d {   }|| j
                  j                  |       d {   }|j                         }d|v r|j                         d   }n|}| j                  j                  d|| j                  j                         d {    n|}| j                  ||      }| t        d| d| d	| d
t        |             t        t        |      S 7 7 7 Pw)NJWT_PUBLIC_KEY_URLz,Missing JWT Public Key URL from environment.litellm_jwt_auth_keyskeys)keyvaluettl)r]   rY   z"No matching public key found. kid=z, keys_url=z, cached_keys=z, len(keys)=)osgetenvrV   r   async_get_cacher   getjsonasync_set_cacher   public_key_ttl
parse_keysr&   r   dict)r   rY   keys_urlcached_keysresponseresponse_jsonr]   
public_keys           r   get_public_keyzJWTHandler.get_public_key   s@    9912JKK 33CC#
 
 !..228<<H$MMOM&$,MMOF$;$))99+((77 :    D__$C_8
4SEXJn]h\iiuvyz~v  vA  B  D*%%3
 =s8   ADD$D(D)A#DDADDDr]   c                    d }t        |      dk(  r\t        |t              r|j                  dd       |k(  s||}|S t        |t              r|d   j                  dd       |k(  s||d   }|S t        |      dkD  rK|D ]F  }t        |t              r|j                  dd       }nd }|+t        |t              s<|?||k(  sE|}H |S )N   rY   r   )r&   r=   ri   rd   rU   )r   r]   rY   rn   r^   key_kids         r   rh   zJWTHandler.parse_keys   s    +/
t9>$%488E4+@C+G3;!
& % D$'QE4(C/3;!!W
  Y]c4(!ggeT2G"GO"3-+3!$J  r   rM   c                     | j                   j                  y|j                  d      d   }|| j                   j                  k(  ryy)NT@F)r   r<   r%   )r   rM   email_domains      r   is_allowed_domainzJWTHandler.is_allowed_domain   sF    99A!'',R04//IIIr   c                   K   g d}t        j                  d      }d }|ddi}dd l}ddlm} |j                  |      }t        j                  d|       |j                  dd       }| j                  |	       d {   }	|	t        |	t              rzi }
d
|	v r|	d
   |
d
<   d|	v r|	d   |
d<   d|	v r|	d   |
d<   d|	v r|	d   |
d<   |j                  t        j                  |
            }	 |j                  |||||| j                         }|S |	t        |	t&              r	 t)        j*                  |	j-                         t/                     }|j1                         j3                  t4        j6                  j8                  t4        j:                  j<                        }|j                  |||||      }|S t%        d      7 C# |j"                  $ r t%        d      t$        $ r}t%        dt'        |             d }~ww xY w# |j"                  $ r t%        d      t$        $ r}t%        dt'        |             d }~ww xY ww)N)RS256RS384RS512PS256PS384PS512JWT_AUDIENCE
verify_audFr   )RSAAlgorithmz
header: %srY   )rY   ktyne)
algorithmsoptionsaudiencer   zToken ExpiredzValidation fails: )r   r   r   zInvalid JWT Submitted)ra   rb   jwtjwt.algorithmsr   get_unverified_headerr	   debugrd   ro   r=   ri   from_jwkre   dumpsdecoder   ExpiredSignatureErrorrV   r>   r   load_pem_x509_certificateencoder   rn   public_bytesr   EncodingPEMPublicFormatSubjectPublicKeyInfo)r   r!   r   r   decode_optionsr   r   headerrY   rn   jwkpublic_key_rsapayloadr   certr^   s                   r   auth_jwtzJWTHandler.auth_jwt   sc     L
99^,*E2N/**51""<8jj%..3.77
!jT&BC
"'.E

"'.E
j %c?Cj %c?C)224::c?CN?**")*%;; %   #
:s(C?55%%'):
 oo'44!**..!..CC **)%* %   /00y 86 ,, 100 ?"4SVH =>>?0 ,, 100 ?"4SVH =>>?s\   A:I<F?=A,I*"G I BH 3I"H $G;;H  I"I%H<<IIc                 T   K   | j                   j                          d {    y 7 wr   )r   closer   s    r   r   zJWTHandler.closeA  s     %%'''s   (&()r   N)r   r   )$__name__
__module____qualname____doc__r   r   __annotations__r
   r   r   intr    r>   r(   rU   boolr,   ri   r   r/   r6   r:   r?   rC   rG   rJ   rN   rR   rX   ro   r   r   rh   rw   r   r    r   r   r   r      s    L))!!	 
-
 &
 )	

 
 

C t  
4 DI 


*23-
	#
T $ 
 
hsm 
QT 
3(4. 3D 3 hsm QT 

*23-
	#
 Xc] xPS}   & &$ &B{ # 8JCW 2C D P1C P1D P1d(r   r   )r   re   ra   typingr   r   r   cryptographyr   cryptography.hazmat.backendsr   cryptography.hazmat.primitivesr   litellm._loggingr	   litellm.caching.cachingr
   'litellm.llms.custom_httpx.httpx_handlerr   litellm.proxy._typesr   r   r   litellm.proxy.utilsr   r   r   r   r   <module>r      s?     	 ' '  8 8 1 - ? I I ,j( j(r   