Ë
    tPêj�3  ã                   óÞ  — U 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Zddlm	Z	 ddl
mZ ddlmZmZ ddlmZmZmZmZmZ ddlmZ ddlmZmZmZ dd	lmZ dd
lmZ  e«       Zee d<    e!h d£«      Z"ee d<   de!e#   fd„Z$de!e#   fd„Z%h d£Z&ee d<   ddde'e#e#f   fd„Z(i Z)ee'e#ef      e d<   de#defd„Z*dZ+ee d<   de#de#dz  de#dz  de#fd„Z,de#de#de'fd„Z-d e'e#e.f   ddfd!„Z/eja                  d"d#g¬$«       ee«      fd%ede1e'e#e#f      fd&„«       Z2eja                  d'd#g¬$«       ee«      d(fd%ede#de'fd)„«       Z3eji                  d*g d+¢d#gd,¬-«       ee«      fde#d.e#d/ed%edef
d0„«       Z5y)1u-  
Plugin proxy routes for litellm.

Enables external services to register as plugins and be proxied through
the litellm proxy server.

Config (in litellm config.yaml general_settings):
  plugins:
    - name: my-plugin
      url: "http://localhost:3210"
      display_name: "My Plugin"
      plugin_key: "sk-..."   # optional: plugin's own auth key

Plugin iframe auth:
  The UI calls GET /api/plugins/auth-token to receive a short-lived identity
  claim ({user_id, user_role, plugin, exp}) encrypted with a per-plugin key
  derived as HMAC-SHA256(LITELLM_SALT_KEY, plugin_name).  The claim carries no
  litellm bearer token, so a compromised plugin learns only the caller's
  identity, never their credential.  LITELLM_SALT_KEY itself is never shared
  with plugins â€” each plugin holds only its own derived key.
é    N)ÚMapping)ÚFinal)ÚFernetÚInvalidToken)Ú	APIRouterÚDependsÚHTTPExceptionÚRequestÚResponse)Úget_async_httpx_client)ÚPluginConfigÚSpecialHeadersÚUserAPIKeyAuth)Úuser_api_key_auth)ÚhttpxSpecialProviderÚrouter>   ÚteÚhostÚcookieÚupgradeÚtrailersÚ
connectionútransfer-encodingÚ_HOP_BY_HOP_STRIPÚreturnc                  ó  — 	 ddl m}  t	        | dd«      }t        |t        «      s
t        «       S |j                  d«      }t        |t        «      r|rt        |j                  «       h«      S t        «       S # t        $ r t        «       cY S w xY w)zçThe lowercased general_settings.litellm_key_header_name, if configured.

    Read live from the proxy module (not import-time) so a custom key header set
    via config is honoured without a restart.  Returns empty when unset.
    r   )Úproxy_serverÚgeneral_settingsNÚlitellm_key_header_name)
Úlitellm.proxyr   Ú	ExceptionÚ	frozensetÚgetattrÚ
isinstanceÚdictÚgetÚstrÚlower)r   r   Únames      ú`/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/plugin_routes.pyÚ_configured_key_header_namesr+   ;   s~   € ðÝ.ô & lÐ4FÈÓMÐÜÐ&¬Ô-Ü‹{ÐØ*×.Ñ.Ð/HÓI€DÜ(2°4¼Ô(=Á$Œ9�d—j‘j“l�^Ó$ÐWÌIËKÐWøô ò Ü‹{Òðús   ‚A6 Á6BÂBc                  óN   — t         t        j                  «       z  t        «       z  S )u‹  Headers to drop before forwarding a request to a plugin backend.

    Every header user_api_key_auth accepts as a litellm credential is stripped â€”
    Authorization, x-api-key, API-Key, x-goog-api-key, Ocp-Apim-Subscription-Key,
    x-litellm-api-key, and any configured custom key header â€” so a plugin can
    never be handed the caller's live litellm key (confused-deputy escalation).
    )r   r   Úlitellm_credential_header_namesr+   © ó    r*   Ú_request_strip_headersr0   L   s!   € ô œ~×MÑMÓOÑOÔRnÓRpÑpÐpr/   >   ú
set-cookieúcontent-lengthúcontent-encodingr   Ú_RESPONSE_STRIPÚrawzMapping[str, str]c                 ó’   — i | j                  «       D ��ci c]  \  }}|j                  «       t        vsŒ||“Œ! c}}¥dddœ¥S c c}}w )a  Strip wire-encoding/cookie headers and force proxied responses inert.

    Plugin-controlled bytes are served from the litellm dashboard origin, so a
    compromised plugin could return an HTML/JS document that executes with the
    admin's session against same-origin management APIs.  A sandbox CSP forces
    the response into an opaque origin with scripts disabled, and nosniff stops
    content-type confusion from re-enabling execution.  Both are set last so a
    plugin cannot override them with its own headers.
    ÚsandboxÚnosniff)zcontent-security-policyzx-content-type-options)Úitemsr(   r4   )r5   ÚkÚvs      r*   Ú_safe_response_headersr<   d   sH   € ðØŸI™I›K×
L‘D�A�q¨1¯7©7«9¼OÒ+Kˆ1ˆa‰4Ó
Lðà#,Ø"+òð ùÛ
Ls
   •A´AÚ_plugin_registryÚplugin_namec                 ó  — t        j                  dd«      j                  «       }t        j                  || j                  «       t
        j                  «      j                  «       }t        t        j                  |«      «      S )zûReturn a Fernet cipher whose key is scoped to a specific plugin.

    Key material: HMAC-SHA256(LITELLM_SALT_KEY, plugin_name).
    A plugin possessing its own key cannot derive the master salt or
    forge claims intended for a different plugin.
    ÚLITELLM_SALT_KEYÚ )ÚosÚgetenvÚencodeÚ_hmacÚnewÚhashlibÚsha256Údigestr   Úbase64Úurlsafe_b64encode)r>   ÚsaltÚderiveds      r*   Ú_plugin_fernetrN      s\   € ô —)‘)Ð.°Ó3×:Ñ:Ó<€DÜ—Y‘Y˜t [×%7Ñ%7Ó%9¼7¿>¹>ÓJ×QÑQÓS€GÜ”&×*Ñ*¨7Ó3Ó4Ð4r/   é   Ú_CLAIM_TTL_SECONDSÚuser_idÚ	user_rolec                 óö   — | |xs d|xs dt        t        j                  «       «      t        z   dœ}t        | «      j	                  t        j                  |«      j                  «       «      j                  «       S )u  Issue a short-lived, audience-scoped identity claim for the plugin.

    The claim contains {user_id, user_role, plugin, exp}.  Crucially it
    contains NO litellm bearer token â€” the plugin can only derive the
    caller's identity, not act as them against the proxy.
    rA   )ÚpluginrQ   rR   Úexp)	ÚintÚtimerP   rN   ÚencryptÚjsonÚdumpsrD   Údecode)r>   rQ   rR   Úclaims       r*   Úissue_plugin_session_claimr]   Ž   sc   € ð Ø’=˜bØ’_ "Ü”4—9‘9“;ÓÔ"4Ñ4ñ	€Eô ˜+Ó&×.Ñ.¬t¯z©z¸%Ó/@×/GÑ/GÓ/IÓJ×QÑQÓSÐSr/   Ú
ciphertextc                 óš  — 	 t        | «      j                  |j                  «       t        ¬«      }t	        j
                  |«      }|j                  d«      | k7  rt        d«      ‚t        |j                  dd«      «      t        t        j                  «       «      k  rt        d«      ‚|S # t        t        f$ r}t        d«      |‚d}~ww xY w)	z½Verify and decode a plugin session claim.

    Raises ValueError if the HMAC is invalid, the audience is wrong, or
    the claim is expired.  Returns the decoded claim dict on success.
    )Úttlz2Invalid, tampered, or expired plugin session claimNrT   zPlugin claim audience mismatchrU   r   zPlugin session claim expired)rN   ÚdecryptrD   rP   rY   Úloadsr   r!   Ú
ValueErrorr&   rV   rW   )r>   r^   r5   r\   Úexcs        r*   Úverify_plugin_session_claimre   ž   s´   € ðXÜ# KÓ0×8Ñ8¸×9JÑ9JÓ9LÔRdÐ8ÓeˆÜ—z‘z #“ˆð ‡y�y�Ó˜kÒ)ÜÐ9Ó:Ð:Ü
ˆ5�9‰9�U˜AÓÓ¤#¤d§i¡i£kÓ"2Ò2ÜÐ7Ó8Ð8Ø€Løô œ)Ð$ò XÜÐMÓNÐTWÐWûðXús   ‚AB* Â*C
Â9CÃC
r   c                 óì   — | j                  d«      }t        |t        «      r|ng }d„ |D «       D �ci c]  }|j                  |“Œ }}t        j                  «        t        j                  |«       yc c}w )z¶Replace the plugin registry from general_settings.

    Replaces (not merges) so plugins removed from config are immediately
    unreachable without requiring a process restart.
    Úpluginsc              3   óF   K  — | ]  }t        j                  |«      –— Œ y ­w)N)r   Úmodel_validate)Ú.0Úentrys     r*   ú	<genexpr>z/register_plugins_from_config.<locals>.<genexpr>¼   s   è ø€ Ò.gÐV[¬|×/JÑ/JÈ5×/QÑ.gùs   ‚!N)r&   r$   Úlistr)   r=   ÚclearÚupdate)r   r5   ÚentriesÚpÚnew_registrys        r*   Úregister_plugins_from_configrs   ´   se   € ð "×%Ñ% iÓ0€CÜ*4°S¼$Ô*?¡3ÀR€GÙ.gÐ_fÔ.gÖh¨˜1Ÿ6™6 1™9Ðh€LÐhÜ×ÑÔÜ×Ñ˜LÕ)ùò is   ±A1z/api/pluginsrg   )ÚtagsÚuser_api_key_dictc              ƒ   ó¸   K  — t         j                  «       D �cg c]4  }|j                  |j                  xs |j                  |j                  dœ‘Œ6 c}S c c}w ­w)u^  Return registered plugins for authenticated UI callers.

    plugin_key is never returned â€” the browser never needs it (the proxy injects
    it server-side from the registry), and exposing it here would leak the
    credential into React state and DevTools.  Admin key management goes through
    the redacted /config/field/info path instead.
    )r)   Údisplay_nameÚurl)r=   Úvaluesr)   rw   rx   )ru   rT   s     r*   Úlist_pluginsrz   Ä   sU   è ø€ ô" '×-Ñ-Ó/öð ð	 —K‘KØ"×/Ñ/Ò>°6·;±;Ø—:‘:ó	
òð ùò ùs   ‚A™9AÁAz/api/plugins/auth-tokenzlitellm-platform-pluginc              ƒ   óÒ   K  — t        j                  d«      st        dd¬«      ‚|t        vrt        dd|› d�¬«      ‚t	        | dd	«      }t	        | d
d	«      }dt        |||«      iS ­w)uû  Issue a short-lived, audience-scoped plugin session claim.

    The claim contains {user_id, user_role, plugin, exp}.  It does NOT
    contain the caller's litellm bearer token â€” a compromised plugin can
    only learn the caller's identity, not impersonate them against the proxy.

    Encrypted with a key derived from HMAC(LITELLM_SALT_KEY, plugin_name),
    so each plugin holds only its own key and cannot forge claims for others.

    Requires LITELLM_SALT_KEY to be set; returns 503 otherwise.
    r@   i÷  zCLITELLM_SALT_KEY is not configured; plugin iframe auth unavailable.)Ústatus_codeÚdetailé”  úPlugin 'z' is not registered.rQ   NrR   Úsession_claim)rB   rC   r	   r=   r#   r]   )ru   r>   rQ   rR   s       r*   Úplugin_auth_tokenr�   Ù   s}   è ø€ ô  �9‰9Ð'Ô(ÜØØXô
ð 	
ð Ô*Ñ*Ü¨°h¸{¸mÐK_Ð4`ÔaÐaÜÐ.°	¸4Ó@€GÜÐ0°+¸tÓD€IØÔ7¸ÀWÈiÓXÐYÐYùs   ‚A%A'z'/plugin-proxy/{plugin_name}/{path:path})ÚGETÚPOSTÚPUTÚDELETEÚPATCHÚOPTIONSÚHEADF)Úmethodsrt   Úinclude_in_schemaÚpathÚrequestc              ƒ   ó   K  — t        |dd«      dk7  rt        dd¬«      S t        j                  | «      }|st        d| › d�d	¬«      S |j                  j                  d
«      › d
|› �}|j                  j                  }|r|› d|› �}|j                  «       ƒ d{  –—† }t        «       }|j                  j                  «       D �	�
ci c]  \  }	}
|	j                  «       |vsŒ|	|
“Œ }}	}
|j                  }|rd|› �|d<   t        |dd«      }t        |dd«      }|rt        |«      |d<   |rt        |«      |d<   t        t        j                   ¬«      }	 |j"                  j%                  |j&                  |||¬«      }|j"                  j)                  |d¬«      ƒ d{  –—† }t        |j,                  |j.                  t1        |j                  «      ¬«      S 7 �Œ=c c}
}	w 7 ŒB# t*        $ r  t        d| › d|j                  › �d¬«      cY S w xY w­w)ué  Authenticated reverse-proxy to a registered plugin backend.

    Restricted to proxy_admin callers â€” the shared plugin_key must not be
    usable as a confused-deputy credential by regular users.  Plugin UIs
    talk to the plugin service directly via the iframe; this route is for
    administrative and server-to-server access only.

    The caller's litellm credential is stripped and replaced with the
    plugin's own plugin_key so plugins never receive a live litellm API key.
    rR   NÚproxy_adminz.Plugin proxy access requires proxy_admin role.i“  )Úcontentr|   r   z' not registeredr~   ú/ú?zBearer ÚauthorizationrQ   zx-litellm-user-idzx-litellm-user-role)Úllm_provider)Úmethodrx   Úheadersr�   F)Úfollow_redirectszCannot connect to plugin 'z' at iö  )r�   r|   r•   )r#   r   r=   r&   rx   ÚrstripÚqueryÚbodyr0   r•   r9   r(   Ú
plugin_keyr'   r   r   ÚPassThroughEndpointÚclientÚbuild_requestr”   Úsendr!   r�   r|   r<   )r>   r‹   rŒ   ru   rT   Ú
target_urlr˜   r™   Ústripr:   r;   Úforward_headersrš   rQ   rR   ÚhandlerÚreqÚresps                     r*   Úplugin_proxyr¥   õ   s"  è ø€ ô, Ð  +¨tÓ4¸ÒEÜØDØô
ð 	
ô
 %×(Ñ(¨Ó5€FÙÜØ˜{˜mÐ+;Ð<Øô
ð 	
ð
 —J‘J×%Ñ% cÓ*Ð+¨1¨T¨FÐ3€JØ—;‘;×$Ñ$€EÙØ"�| 1 U GÐ,ˆ
àŸ™›×&€Dô *Ó+€EØ/6¯©×/DÑ/DÓ/F×a¡t q¨!È!Ï'É'Ë)Ð[`ÒJ`˜a ™dÐa€OÑað ×)Ñ)€JÙØ-4°Z°LÐ+Aˆ˜Ñ(ô
 Ð.°	¸4Ó@€GÜÐ0°+¸tÓD€IÙÜ/2°7«|ˆÐ+Ñ,ÙÜ14°Y³ˆÐ-Ñ.ä+Ô9M×9aÑ9aÔb€Gð
Ø—^‘^×1Ñ1Ø—>‘>ØØ#Øð	 2ó 
ˆð $ŸN™N×/Ñ/°ÀeÐ/ÓL×Lˆô Ø—‘Ø×$Ñ$Ü& t§|¡|Ó4ôð ðM 'ûó bð6 MùÜò 
ÜØ0°°¸UÀ6Ç:Á:À,ÐOØô
ò 	
ð
üsb   ‚BHÂGÂ+HÃGÃ!GÃ&A-HÅA	G" ÆG ÆG" Æ"6HÇHÇ G" Ç"&HÈHÈ
HÈH)6Ú__doc__rJ   rG   ÚhmacrE   rY   rB   rW   Úcollections.abcr   Útypingr   Úcryptography.fernetr   r   Úfastapir   r   r	   r
   r   Ú&litellm.llms.custom_httpx.http_handlerr   Úlitellm.proxy._typesr   r   r   Ú$litellm.proxy.auth.user_api_key_authr   Úlitellm.types.llms.custom_httpr   r   Ú__annotations__r"   r   r'   r+   r0   r4   r%   r<   r=   rN   rP   r]   re   Úobjectrs   r&   rm   rz   r�   Ú	api_router¥   r.   r/   r*   ú<module>r³      s]  ðòó, Û Û Û Û 	Û Ý #Ý ç 4ß HÕ Hå Iß MÑ MÝ BÝ ?á“€ˆÓ ñ %òó
Ð �5ó 
ðX i°¡nó Xð"q 	¨#¡ó qò €�ó ðÐ 3ð ¸¸SÀ#¸X¹ó ð$ 46Ð �%˜˜S ,Ð.Ñ/Ñ0Ó 5ð	5 ð 	5¨ó 	5ð Ð �EÓ ðT¨Cð T¸#À¹*ð TÐQTÐW[ÑQ[ð TÐ`có Tð ¨Sð ¸cð Àdó ð,
*°4¸¸V¸Ñ3Dð 
*Èó 
*ð  ‡�ˆN ) €Ó-á(/Ð0AÓ(BñØ%ðà	ˆ$ˆs�Cˆx‰.Ñòó .ðð( ‡�Ð%¨Y¨K€Ó8á(/Ð0AÓ(BØ0ñZØ%ðZàðZð 
òZó 9ðZð6 ×ÑØ-ÚHØ
ˆØð	 ó ñ )0Ð0AÓ(Bñ	LØðLà
ðLð ðLð &ð	Lð
 òLóñLr/   