o
    ưiy                     @   sd  d Z ddlZddlZddlZddlmZmZ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 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m Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-m.Z. ddl/m0Z0 ddl1m2Z2m3Z3 ddl4m5Z5m6Z6m7Z7m8Z8m9Z9m:Z:m;Z;m<Z<m=Z=m>Z>m?Z?m@Z@ G dd dZAG dd dZBdS )z
Supports using JWT's for authenticating into the proxy.

Currently only supports admin.

JWT token must have 'litellm_proxy_admin' in scope.
    N)AnyListLiteralOptionalSetTuplecast)x509)default_backend)serializationHTTPException)verbose_proxy_logger)	DualCache)get_nested_value)HTTPHandler)
RBAC_ROLESJWKKeyValueJWTAuthBuilderResult
JWTKeyItemLiteLLM_EndUserTableLiteLLM_JWTAuthLiteLLM_OrganizationTableLiteLLM_TeamMembershipLiteLLM_TeamTableLiteLLM_UserTableLitellmUserRolesMemberProxyErrorTypesProxyExceptionScopeMappingSpanTeamMemberAddRequestUserAPIKeyAuth)can_team_access_model)PrismaClientProxyLogging   )_allowed_routes_checkallowed_routes_checkget_actual_routesget_end_user_objectget_org_objectget_org_object_by_aliasget_role_based_modelsget_role_based_routesget_team_membershipget_team_objectget_team_object_by_aliasget_user_objectc                   @   s  e Zd ZU dZee ed< eed< 		dNddZ	dOdee ded	e	d
e
ddf
ddZedefddZdedee fddZdedee fddZdedefddZdedee fddZdedee dee fddZdefddZdefddZdedee dee fd d!Zdedee dee fd"d#ZdPd$ee defd%d&Zdedee dee fd'd(Zdedeee  deee  fd)d*Zdedee  fd+d,Z!dedeee  deee  fd-d.Z"d/eee  defd0d1Z#dedee dee fd2d3Z$dedee dee fd4d5Z%dedee dee fd6d7Z&dedee dee fd8d9Z'dedee fd:d;Z(d<edefd=d>Z)d?ee defd@dAZ*dBe+d?ee dee, fdCdDZ-dEedefdFdGZ.dedefdHdIZ/dedefdJdKZ0dLdM Z1dS )Q
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                 C   s   t  | _d| _d S )Nr   )r   http_handlerleewayself r<   T/home/app/Keep/.python/lib/python3.10/site-packages/litellm/proxy/auth/handle_jwt.py__init__I   s   
zJWTHandler.__init__r   litellm_jwtauthr9   c                 C   s   || _ || _|| _|| _d S N)r5   r6   r?   r9   )r;   r5   r6   r?   r9   r<   r<   r=   update_environmentO   s   
zJWTHandler.update_environmenttokenc                 C   s   |  d}t|dkS )N.   )splitlen)rB   partsr<   r<   r=   is_jwt[   s   
zJWTHandler.is_jwtc                 C   sT   | j jdu rdS | j|dd}|sdS t|}| j jD ]}|j|v r'|j  S qdS )a  
        Returns the RBAC role the token 'belongs' to based on role mappings.

        Args:
            token (dict): The JWT token containing role information

        Returns:
            Optional[RBAC_ROLES]: The mapped internal RBAC role if a mapping exists,
                                None otherwise

        Note:
            The function handles both single string roles and lists of roles from the JWT.
            If multiple mappings match the JWT roles, the first matching mapping is returned.
        NrB   default_value)r?   Zrole_mappingsget_jwt_rolesetroleZinternal_role)r;   rB   jwt_roleZjwt_role_setZrole_mappingr<   r<   r=   _rbac_role_from_role_mapping`   s   

z'JWTHandler._rbac_role_from_role_mappingc                 C   s   | j |d}| j|d}| j|dd}|rtjS | j|dddur$tjS | j|dddur0tjS |dur=| j	|dr=tjS | j
|d }rG|S dS )a^  
        Returns the RBAC role the token 'belongs' to.

        RBAC roles allowed to make requests:
        - PROXY_ADMIN: can make requests to all routes
        - TEAM: can make requests to routes associated with a team
        - INTERNAL_USER: can make requests to routes associated with a user

        Resolves: https://github.com/BerriAI/litellm/issues/6793

        Returns:
        - PROXY_ADMIN: if token is admin
        - TEAM: if token is associated with a team
        - INTERNAL_USER: if token is associated with a user
        - None: if token is not associated with a team or user
        rB   scopesNrI   )
user_roles)
get_scopesis_adminget_user_rolesr   PROXY_ADMINget_team_idTEAMget_user_idINTERNAL_USERis_allowed_user_rolerO   )r;   rB   rR   rU   rS   	rbac_roler<   r<   r=   get_rbac_role   s    zJWTHandler.get_rbac_rolerR   c                 C   s   | j j|v rdS dS )NTF)r?   Zadmin_jwt_scope)r;   rR   r<   r<   r=   rU      s   zJWTHandler.is_adminc                 C   s*   | j jd urt|| j jg d}|pg S g S NdataZkey_pathdefault)r?   Zteam_ids_jwt_fieldr   )r;   rB   team_idsr<   r<   r=   get_team_ids_from_jwt   s   z JWTHandler.get_team_ids_from_jwtrJ   c                 C   H   z| j jd urt|| j j|d}W |S d }W |S  ty#   |}Y |S w r_   )r?   Zend_user_id_jwt_fieldr   KeyErrorr;   rB   rJ   user_idr<   r<   r=   get_end_user_id   s   
zJWTHandler.get_end_user_idc                 C   s    | j jdu r| j jdu rdS dS )z
        Returns:
        - True: if 'team_id_jwt_field' or 'team_alias_jwt_field' is set
        - False: if neither is set
        NFT)r?   team_id_jwt_fieldteam_alias_jwt_fieldr:   r<   r<   r=   is_required_team_id   s   zJWTHandler.is_required_team_idc                 C   s"   | j jdurt| j jtrdS dS )z
        Returns:
        - True: if 'user_allowed_email_domain' is set
        - False: if 'user_allowed_email_domain' is None
        NTF)r?   user_allowed_email_domain
isinstancestrr:   r<   r<   r=   is_enforced_email_domain   s
   z#JWTHandler.is_enforced_email_domainc              	   C   s   zZ| j jd urIt }t|| j j|d}||u r%| j jd ur"| j jW S |W S t|trF|s/|W S td| j j d| d|d  d |d }|W S | j jd urV| j j}W |S d }W |S  t	yf   |}Y |S w )Nr`   zJWT Auth: team_id_jwt_field 'z' returned a list z; using first element 'r   z' automatically.)
r?   rj   objectr   Zteam_id_defaultrn   listr   debugrf   )r;   rB   rJ   sentinelteam_idr<   r<   r=   rX      sB   


zJWTHandler.get_team_idc                 C   H   z| j jdurt|| j j|d}|W S d}W |S  ty#   |}Y |S w )aG  
        Extract team name/alias from JWT token using the configured team_alias_jwt_field.

        Args:
            token: The decoded JWT token dictionary
            default_value: Default value to return if field not found

        Returns:
            The team alias from the token, or default_value if not found
        Nr`   )r?   rk   r   rf   )r;   rB   rJ   
team_aliasr<   r<   r=   get_team_alias  s   zJWTHandler.get_team_aliasvalid_user_emailc                 C   s   |du rdS | 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;   ry   r<   r<   r=   is_upsert_user_id  s   zJWTHandler.is_upsert_user_idc                 C   H   z| j jd urt|| j j|d}W |S |}W |S  ty#   |}Y |S w r_   )r?   Zuser_id_jwt_fieldr   rf   rg   r<   r<   r=   rZ   '     	zJWTHandler.get_user_idc                 C   H   z| j jdurt|| j j|d}W |S |}W |S  ty#   |}Y |S w )zn
        Returns the user role from the token.

        Set via 'user_roles_jwt_field' in the config.
        Nr`   )r?   Zuser_roles_jwt_fieldr   rf   r;   rB   rJ   rS   r<   r<   r=   rV   5  s   	zJWTHandler.get_user_rolesc                 C   sZ   | j jsdS | j|g d}|sdS | j jD ]}|D ]}t||jr)|j    S qqdS )z(Map roles from JWT to LiteLLM user rolesNrI   )r?   Zjwt_litellm_role_maprK   fnmatchrN   Zlitellm_role)r;   rB   Z	jwt_rolesmappingrM   r<   r<   r=   map_jwt_role_to_litellm_roleJ  s   z'JWTHandler.map_jwt_role_to_litellm_rolec                 C   r~   )z
        Generic implementation of `get_user_roles` that can be used for both user and team roles.

        Returns the jwt role from the token.

        Set via 'roles_jwt_field' in the config.
        Nr`   )r?   Zroles_jwt_fieldr   rf   r   r<   r<   r=   rK   Y  s   
	zJWTHandler.get_jwt_rolerS   c                    s2   |dur j jdurt fdd|D rdS dS )zl
        Returns the user role from the token.

        Set via 'user_allowed_roles' in the config.
        Nc                 3   s    | ]	}| j jv V  qd S r@   )r?   user_allowed_roles).0rM   r:   r<   r=   	<genexpr>y  s    
z2JWTHandler.is_allowed_user_role.<locals>.<genexpr>TF)r?   r   any)r;   rS   r<   r:   r=   r\   p  s   zJWTHandler.is_allowed_user_rolec                 C   re   r_   )r?   Zuser_email_jwt_fieldr   rf   )r;   rB   rJ   
user_emailr<   r<   r=   get_user_email  s   	zJWTHandler.get_user_emailc                 C   r|   r_   )r?   Zobject_id_jwt_fieldr   rf   )r;   rB   rJ   	object_idr<   r<   r=   get_object_id  r}   zJWTHandler.get_object_idc                 C   re   r_   )r?   Zorg_id_jwt_fieldr   rf   )r;   rB   rJ   org_idr<   r<   r=   
get_org_id  r}   zJWTHandler.get_org_idc                 C   rv   )aV  
        Extract organization name/alias from JWT token using the configured org_alias_jwt_field.

        Args:
            token: The decoded JWT token dictionary
            default_value: Default value to return if field not found

        Returns:
            The organization alias from the token, or default_value if not found
        Nr`   )r?   Zorg_alias_jwt_fieldr   rf   )r;   rB   rJ   	org_aliasr<   r<   r=   get_org_alias  s   zJWTHandler.get_org_aliasc                 C   sn   z*t |d tr|d  }W |S t |d tr|d }W |S tdt|d  d ty6   g }Y |S w )NscopezUnmapped scope type - z. Supported types - list, str.)rn   ro   rE   rr   	Exceptiontyperf   )r;   rB   rR   r<   r<   r=   rT     s   	
zJWTHandler.get_scopesurlc              
      s  d|vr|S d| }| j |I dH }|dur|S td|  | j|I dH }|jdkr@td| d|j d|j z|	 }W n ty\ } z
td	| d| d}~ww |d
}|sltd| dtd| d|  | j j
||| jjdI dH  |S )aS  
        If url points to an OIDC discovery document (*.well-known/openid-configuration),
        fetch it and return the jwks_uri contained within.  Otherwise return url unchanged.
        This lets JWT_PUBLIC_KEY_URL be set to a well-known discovery endpoint instead of
        requiring operators to manually find the JWKS URL.
        z .well-known/openid-configurationZlitellm_oidc_discovery_Nz0JWT Auth: Fetching OIDC discovery document from    z"JWT Auth: OIDC discovery endpoint z returned status : z5JWT Auth: Failed to parse OIDC discovery document at jwks_uriz%JWT Auth: OIDC discovery document at z% does not contain a 'jwks_uri' field.z"JWT Auth: Resolved OIDC discovery z -> jwks_uri=keyvaluettl)r6   async_get_cacher   rs   r8   getstatus_coder   textjsonasync_set_cacher?   public_key_ttl)r;   r   	cache_keyZcached_jwks_uriresponseZ	discoveryer   r<   r<   r=   _resolve_jwks_url  sJ   



zJWTHandler._resolve_jwks_urlkidc                    sB  t d}|d u rtddd |dD }|D ]|}| |I d H }d| }| j|I d H }|d u r| j|I d H }z|	 }W n  tye }	 zt
d|	 d|j  td|	 d	d }	~	ww d
|v rq|	 d
 }
n|}
| jj||
| jjdI d H  n|}
| j|
|d}|d urtt|  S qtd| d| )NZJWT_PUBLIC_KEY_URLz,Missing JWT Public Key URL from environment.c                 S   s   g | ]}|  qS r<   )strip)r   r   r<   r<   r=   
<listcomp>  s    z-JWTHandler.get_public_key.<locals>.<listcomp>,Zlitellm_jwt_auth_keys_zError parsing response: z. Original Response: z*. Check server logs for original response.keysr   )r   r   z#No matching public key found. keys=z, kid=)osgetenvr   rE   r   r6   r   r8   r   r   r   errorr   r   r?   r   
parse_keysr   dict)r;   r   Zkeys_urlZkeys_url_listZkey_urlr   Zcached_keysr   Zresponse_jsonr   r   
public_keyr<   r<   r=   get_public_key  sL   


zJWTHandler.get_public_keyr   c                 C   s   d }t |dkr6t|tr|dd |ks|d u r|}|S t|tr4|d dd |ks0|d u r4|d }|S t |dkrb|D ]#}t|trL|dd }nd }|d urat|tra|d ura||kra|}q>|S )Nr'   r   r   )rF   rn   r   r   rr   )r;   r   r   r   r   Zkey_kidr<   r<   r=   r   2  s,   "

zJWTHandler.parse_keysr   c                 C   s2   | j jd u rdS |dd }|| j jkrdS dS )NT@F)r?   rm   rE   )r;   r   Zemail_domainr<   r<   r=   is_allowed_domainK  s   zJWTHandler.is_allowed_domainc              
      s"  | j js	tdd|dd  }| j|I dH }|dur&td |S td| j j  zA| jj| j jd| dd	d
I dH }|j	dkrUtd|j	 d|j
 | }td|  | jj||| j jdI dH  |W S  ty } ztdt|  tdt| d}~ww )a  
        Fetch user information from OIDC UserInfo endpoint.

        This follows the OpenID Connect protocol where an access token
        is sent to the identity provider's UserInfo endpoint to retrieve
        user identity information.

        Args:
            token: The access token to use for authentication

        Returns:
            dict: User information from the UserInfo endpoint

        Raises:
            Exception: If UserInfo endpoint is not configured or request fails
        zWOIDC UserInfo endpoint not configured. Set 'oidc_userinfo_endpoint' in JWT auth config.Zoidc_userinfo_N   zReturning cached OIDC UserInfoz Calling OIDC UserInfo endpoint: zBearer zapplication/json)AuthorizationAccept)r   headersr   z'OIDC UserInfo endpoint returned status r   zReceived OIDC UserInfo: r   zError fetching OIDC UserInfo: zFailed to fetch OIDC UserInfo: )r?   Zoidc_userinfo_endpointr   r6   r   r   rs   r8   r   r   r   r   r   Zoidc_userinfo_cache_ttlr   ro   )r;   rB   r   Zcached_userinfor   userinfor   r<   r<   r=   get_oidc_userinfoU  sL   

zJWTHandler.get_oidc_userinfoc              
      s$  g d}t d}d }|d u rddi}dd l}ddlm} ||}td| |dd }| j	|d	I d H }	|	d urt
|	tri }
d
|	v rM|	d
 |
d
< d|	v rW|	d |
d< d|	v ra|	d |
d< d|	v rk|	d |
d< d|	v ru|	d |
d< d|	v r|	d |
d< d|	v r|	d |
d< ||
j}z|j|||||| jd}|W S  |jy   td ty } z	tdt| d }~ww |	d urt
|	trz"t|	 t }| tjjtjj}|j|||||d}|W S  |jy   td ty } z	tdt| d }~ww td)N)
ZRS256ZRS384ZRS512ZPS256ZPS384ZPS512ZES256ZES384ZES512ZEdDSAZJWT_AUDIENCEZ
verify_audFr   )PyJWKz
header: %sr   )r   Zktynr   xyZcrv)
algorithmsoptionsaudiencer9   zToken ExpiredzValidation fails: )r   r   r   zInvalid JWT Submitted)r   r   jwtZjwt.api_jwkr   Zget_unverified_headerr   rs   r   r   rn   r   	from_dictr   decoder9   ZExpiredSignatureErrorr   ro   r	   Zload_pem_x509_certificateencoder
   r   public_bytesr   ZEncodingZPEMZPublicFormatZSubjectPublicKeyInfo)r;   rB   r   r   Zdecode_optionsr   r   headerr   r   ZjwkZpublic_key_objpayloadr   certr   r<   r<   r=   auth_jwt  s   


zJWTHandler.auth_jwtc                    s   | j  I d H  d S r@   )r8   closer:   r<   r<   r=   r     s   zJWTHandler.close)r7   N)r   r@   )2__name__
__module____qualname____doc__r   r%   __annotations__r   r>   r   intrA   staticmethodro   rH   r   r   rO   r^   rr   boolrU   r   rd   ri   rl   rp   rX   rx   r{   rZ   rV   r   r   rK   r\   r   r   r   r   rT   r   r   r   r   r   r   r   r   r   r<   r<   r<   r=   r4   >   s   
 

$
$








.0
Cdr4   c                #   @   sD  e Zd ZdZededededed fddZ	ededed	e
e ded fd
dZedee dee dededdf
ddZedededededede
e ddfddZe	d@dededede
e de
e dede
e de
e fddZededede
e dede
e dedee
e e
e f fdd Zedededee fd!d"Zed#ee d$e
e dedede
e dede
e dedee
e e
e f fd%d&Zedededee
e e
e e
e f fd'd(Ze	d@de
e d)e
e de
e d*e
e d+e
e d,e
e dede
e dede
e deded-e
e dee
e  e
e! e
e" e
e# f fd.d/Z$ede
e d+e
e d0ed1eded f
d2d3Z%ed4e
e d5ee de
e fd6d7Z&ed8e
e  d9e
e fd:d;Z'ededed8e
e  de
e ddf
d<d=Z(e	d@dededededede
e dede
e ded4e
e defd>d?Z)dS )AJWTAuthManagerz7Manages JWT authentication and authorization operationsr]   general_settingsrouter7   Tc                 C   sR   t | |d}|du s|du rdS t||d}|s'tdd|  d| d| d	dS )
zU
        Checks if user is allowed to access the route, based on their role.
        r]   r   NT)
user_routeallowed_routes  Role=z not allowed to call route=z. Allowed routes=r   detail)r/   r(   r   )r]   r   r   Zrole_based_routes
is_allowedr<   r<   r=   can_rbac_role_call_route  s   	z'JWTAuthManager.can_rbac_role_call_routemodelc                 C   sJ   t | |d}|du s|du rdS ||vr#tdd|  d| d| ddS )	zU
        Checks if user is allowed to access the model, based on their role.
        r   NTr   r   z not allowed to call model=z. Allowed models=r   )r.   r   )r]   r   r   Zrole_based_modelsr<   r<   r=   can_rbac_role_call_model   s   	z'JWTAuthManager.can_rbac_role_call_modelscope_mappingsrR   request_dataNc                 C   sh   | sdS g }| D ]}|j |v r|jr||j q|d}|s"dS ||vr2tddd||iddS )zE
        Check if scope allows access to the requested model
        Nr   r   r   z'model={} not allowed. Allowed_models={}r   )r   modelsextendr   r   format)r   rR   r   r   Zallowed_modelssmrequested_modelr<   r<   r=   check_scope_based_access7  s&   

z'JWTAuthManager.check_scope_based_accessjwt_handlerjwt_valid_tokenc                    sP   | j jdu r&|du rtdddtj|||dd tj|||d dS dS )	z/Validate RBAC role and model access permissionsTNr   zjUnmatched token passed in. enforce_rbac is set to True. Token must belong to a proxy admin, team, or user.r   r   )r]   r   r   )r]   r   r   )r?   enforce_rbacr   r   r   r   r   )r   r   r   r   r   r]   r<   r<   r=   check_rbac_roleY  s$   

zJWTAuthManager.check_rbac_rolerh   r   api_keyc           
         sr   | j |ds	dS ttj|| jd}|s'| jj}t|d}	td| d|	 tddddd|d|d|d|p6i dS )	z/Check admin status and route access permissionsrQ   N	user_roler   Zlitellm_proxy_roles)r   z.Admin not allowed to access this route. Route=z, Allowed Routes=T)is_proxy_adminteam_objectuser_objectend_user_object
org_objectrB   ru   rh   end_user_idr   team_membership
jwt_claims)	rU   r)   r   rW   r?   Zadmin_allowed_routesr*   r   r   )
r   rR   r   rh   r   r   r   r   r   Zactual_routesr<   r<   r=   check_admin_accesst  s8   
z!JWTAuthManager.check_admin_accessr5   r6   parent_otel_spanproxy_logging_objc                    sd  | j |dd}d}|rt|||||| jjdI dH }||fS | j|dd}|rRtd| d t|||||dI dH }|rR|j}td| d| d ||fS | 	 d	u r| jj
}	| jj}
d
}|	rd|	v r|	dd}|d  r|d }d|	 d| d}nd|	v r|	drtd|	}|r|d}d|	 d| d}td|	 d|
 d| ||fS )zQFind and validate specific team ID from team_id_jwt_field or team_alias_jwt_fieldNrI   ru   r5   r6   r   r   team_id_upsertz$JWT Auth: Resolving team by alias: '')rw   r5   r6   r   r   zJWT Auth: Resolved team_alias='z' to team_id='T rC   r'   r   r   z* Hint: dot-notation array indexing (e.g. 'z') is not supported. Use 'uZ   ' instead — LiteLLM automatically uses the first element when the field value is a list.[]z^(\w+)\[(\d+)\]$z Hint: array indexing (e.g. 'z/') is not supported in team_id_jwt_field. Use 'z/No team found in token. Checked team_id field 'z' and team_alias field 'z'.)rX   r1   r?   r   rx   r   infor2   ru   rl   rj   rk   rsplitisdigitendswithrematchgroupr   )r   r   r5   r6   r   r   Zindividual_team_idr   rw   Zteam_id_fieldZteam_alias_fieldhintrG   Z
base_fieldmr<   r<   r=   "find_and_validate_specific_team_id  sz   


z1JWTAuthManager.find_and_validate_specific_team_idc                 C   s   | j |d}t|}|S )z8Get combined team IDs from groups and individual team_idrP   )rd   rL   )r   r   Zteam_ids_from_groupsall_team_idsr<   r<   r=   get_all_team_ids  s   zJWTAuthManager.get_all_team_idsrc   r   c              	      s   ddl m} | s|jjrtddddS | D ]U}	zJt|	||||dI dH }
|
rb|
jdurb|
j}t|trb|rBt	||
|dd	I dH rbt
tj||jd
}td|	 d| d|  |rb|	|
fW   S W q tyl   Y qw |r|tdd| d|  dddS )z2Find first team with access to the requested modelr   )
llm_routerr   zgNo teams found in token. `enforce_team_based_model_access` is set to True. Token must belong to a team.r   )NN)ru   r5   r6   r   r   N)r   r   r  Zteam_model_aliasesr   zJWT team route check: team_id=z, route=z, is_allowed=z+No team has access to the requested model: z. Checked teams=z.. Check `/models` to see all available models.)Zlitellm.proxy.proxy_serverr  r?   Zenforce_team_based_model_accessr   r1   r   rn   rr   r$   r)   r   rY   r   rs   r   )rc   r   r   r   r5   r6   r   r   r  ru   r   Zteam_modelsr   r<   r<   r=   find_team_with_model_access  sd   

	z*JWTAuthManager.find_team_with_model_accessc                    sL   | j |dd}d}|  r|du rdn| j|d}| j||d}|||fS )z$Get user email and validation statusNrI   F)r   )r   rp   r   rZ   )r   r   r   ry   rh   r<   r<   r=   get_user_info8  s   

zJWTAuthManager.get_user_infor   r   ru   ry   r   c              
      s@  d}|r|rt ||||	|
dI dH nd}n&|r<td| d t||||	|
dI dH }|r<td| d|j d |du rQtd	| d
|jj tj	dddd}| rl| rjt
| |||j|d|	|
|| dI dH nd}d}|r|rt||||	|
|dI dH nd}d}| r|r| r|rt| ||||	|
dI dH nd}||||fS )zTGet user, org, and end user objects. Also resolves org aliases to IDs if configured.N)r   r5   r6   r   r   z#JWT Auth: Resolving org by alias: 'r   )r   r5   r6   r   r   zJWT Auth: Resolved org_alias='z' to org_id='Fz&Email domain not allowed. User email: z. Allowed domain: r   r   )messager   paramcode)ry   )rh   r5   r6   rz   r   r   r   Zsso_user_id)r   r5   r6   r   r   r   )rh   ru   r5   r6   r   r   )r,   r   r  r-   organization_idr   r?   rm   r   Z
auth_errorr3   r{   r+   r0   )rh   r   r   r   ru   ry   r   r5   r6   r   r   r   r   r   r   r   team_membership_objectr<   r<   r=   get_objectsM  s   	

	
	zJWTAuthManager.get_objectsr   r   c                 C   s    |r|s| s|st ddddS )zUIf enforce_rbac is true, validate that a valid rbac id is returned for spend trackingr   zrNo user or team id found in token. enforce_rbac is set to True. Token must belong to a proxy admin, team, or user.r   Tr   rh   ru   r   r   r<   r<   r=   validate_object_id  s   z!JWTAuthManager.validate_object_idrequest_headersallowed_team_idsc                 C   sd   | sdS dd |   D }|d}|sdS ||vr(tdd| dt| dtd	|  |S )
a  
        Extract team_id from x-litellm-team-id header if present.
        Validates that the team is in the user's allowed teams from JWT.

        Args:
            request_headers: Dictionary of request headers
            allowed_team_ids: Set of team IDs the user is allowed to access (from JWT)

        Returns:
            The team_id from header if valid, None otherwise

        Raises:
            HTTPException: If team_id is provided but not in allowed_team_ids
        Nc                 S   s   i | ]	\}}|  |qS r<   )lower)r   kvr<   r<   r=   
<dictcomp>  s    z:JWTAuthManager.get_team_id_from_header.<locals>.<dictcomp>zx-litellm-team-idr   zTeam 'zS' from x-litellm-team-id header is not in your JWT's allowed teams. Allowed teams: r   z-Using team_id from x-litellm-team-id header: )itemsr   r   rr   r   rs   )r  r  Znormalized_headersheader_team_idr<   r<   r=   get_team_id_from_header  s   
z&JWTAuthManager.get_team_id_from_headerr   r   c              
      s   ddl m} | sdS |sdS |jD ]}|jr |j| jkr  dS qtt| jdd|jd}z||ttj	ddI dH  t
d	| j d
|j  W dS  tyr } z|jtjkrlt
d| j d|j  W Y d}~dS |d}~ww )z
        Map user to teams.
        - If user is not in team, add them to the team
        - If user is in team, do nothing
        r   )team_member_addNuser)rh   rM   )memberru   )r   )ra   Zuser_api_key_dictzSuccessfully added user z	 to team zUser z is already a member of team )Z1litellm.proxy.management_endpoints.team_endpointsr!  Zmembers_with_rolesrh   r"   r   ru   r#   r   rW   r   rs   r   r   r   Zteam_member_already_in_team)r   r   r!  r#  ra   r   r<   r<   r=   map_user_to_teams  sJ   

z JWTAuthManager.map_user_to_teamsc           
         s   | j jsdS |du s|du rdS | |}|r4|j|jkr4|jjjd|jid|jidI dH  |j|_t	| 
|}t	|jp@g }|| }|| }|sN|rhddlm}	 |	|jt|t|dI dH  t||_dS )ak  
        Sync user role and team memberships with JWT claims

        The goal of this method is to ensure:
        1. The user role on LiteLLM DB is in sync with the IDP provider role
        2. The user is a member of the teams specified in the JWT token

        This method is only called if sync_user_role_and_teams is set to True in the JWT config.
        Nrh   r   )wherera   r   )patch_team_membership)rh   Zteams_ids_to_add_user_toZteams_ids_to_remove_user_from)r?   sync_user_role_and_teamsr   r   r   dbZlitellm_usertableupdaterh   rL   rd   ZteamsZ/litellm.proxy.management_endpoints.scim.scim_v2r&  rr   )
r   r   r   r5   Znew_roleZjwt_team_idsZexisting_teamsZteams_to_addZteams_to_remover&  r<   r<   r=   r'  )  s2   

z'JWTAuthManager.sync_user_role_and_teamsc
                     s  |j jrtd |j| dI dH }
n	|j| dI dH }
|j jr-|j |
s-tddd|j|
d}t	
||
||||I dH  |j|
d}|j jrY|j jrYt	j|j j|||d |j|
dd}|j|
d}t	||
I dH \}}}|j|
dd}|j|
dd}d}d}|j|
dd}|r|r|tjkr|}n|tjkr|}t	|||||| |
I dH }|r|S t	||
}|j|
dd}|r|| t	j|	|d	}|r|}t||||||j jd
I dH }n|st	||
||||I dH \}}|s|st	j||d||||||dI dH \}}|j |
dd}t	j!|||||||||||||dI dH \}}}}|r1|j"n|}t	j#||
||dI dH  t	j$||dI dH  t	j%|||dddd t&|o_|j'tj(k}t)|||||||||| ||
dS )z-Main authentication and authorization builderzDOIDC UserInfo is enabled. Fetching user info from UserInfo endpoint.rP   Nr   zInvalid JWT tokenr   )r   rR   r   r   rI   )r  r  r   r   )rc   r   r   r   r5   r6   r   r   )rh   r   r   r   ru   ry   r   r5   r6   r   r   r   r   )r   r   r   r5   )r   r   r   Fr  )r   ru   r   rh   r   r   r   r   r   rB   r   r   )*r?   Zoidc_userinfo_enabledr   rs   r   r   Zcustom_validater   r^   r   r   rT   Zenforce_scope_based_accessr   r   r   r  r   ri   r   rY   r[   r   r  rX   addr   r1   r   r
  r  r   r   r  r  r'  r$  r  r   r   rW   r   ) r   r   r   r   r   r5   r6   r   r   r  r   r]   rR   r   rh   r   ry   r   r   ru   r   Zadmin_resultr  Zspecific_team_idr  r   r   r   r   r  Zresolved_org_idr   r<   r<   r=   auth_builderZ  s2  








zJWTAuthManager.auth_builderr@   )*r   r   r   r   r   r   r   ro   r   r   r   r   r   r    r   r4   r   rr   r   r   r%   r   r!   r&   r   r   r
  r   r  r  r   r  r   r   r   r   r  r  r   r$  r'  r+  r<   r<   r<   r=   r      s   !(Q	?	
p(20	
r   )Cr   r   r   r  typingr   r   r   r   r   r   r   Zcryptographyr	   Zcryptography.hazmat.backendsr
   Zcryptography.hazmat.primitivesr   Zfastapir   Zlitellm._loggingr   Zlitellm.caching.cachingr   Z0litellm.litellm_core_utils.dot_notation_indexingr   Z'litellm.llms.custom_httpx.httpx_handlerr   Zlitellm.proxy._typesr   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r!   r"   r#   Zlitellm.proxy.auth.auth_checksr$   Zlitellm.proxy.utilsr%   r&   Zauth_checksr(   r)   r*   r+   r,   r-   r.   r/   r0   r1   r2   r3   r4   r   r<   r<   r<   r=   <module>   s0    $P8     G