Ë
    tPêjA§ ã                   ó�  — U d Z ddlZddl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mZmZmZmZ dd	lmZmZmZmZ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'Z'ddl(m)Z) ddl*m+Z+ ddl,m-Z- ddl.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5m6Z6m7Z7m8Z8m9Z9m:Z: ddl;m<Z< ddl=m>Z> ddl?m@Z@ ddlAmBZB ddlCmDZD ddlEmFZFmGZGmHZH ddlImJZJmKZKmLZL ddlMmNZNmOZOmPZP ddlQmRZR ddlSmTZT ddlUmVZV ddlW­ erddlXmYZZ  G d„ de«      Z[ G d„ d e«      Z\ G d!„ d"e«      Z] G d#„ d$e«      Z^ed%eTd&e[fd'„«       Z_ed%eRd&e\fd(„«       Z_ed%eVd&e]fd)„«       Z_ed%eNeOz  ePz  d&e^fd*„«       Z_d%eTeRz  eVz  eNz  eOz  ePz  d&e`fd+„Z_ G d,„ d-«      Za G d.„ d/e%«      Zb G d0„ d1e «      Zc ed2d3g eeK«      g¬4«      Zdeeed5<   d6„ Zfd7egd&e0fd8„Zhd9egd&e/fd:„Zid;ejd&ebfd<„Zk	 	 	 	 �dd=egdz  d>egdz  d?eldz  d@emdz  dAeneo   dz  dBeneo   dz  d&epege`f   fdC„Zqd&elfdD„Zrese1jè                  e1jê                  e1jì                  e1jî                  f   Zxd&exfdE„Zyd&egdz  fdF„ZzdGene{   dHegdz  dIexd&e1dz  fdJ„Z|dKeJdLeneg   d&ene{   fdM„Z}dKeJdNeeg   d&dfdO„Z~ G dP„ dQe«      Z G dR„ dSe«      Z€ G dT„ dUe«      Z� G dV„ dWe«      Z‚eƒee€e�e‚f   Z„ G dX„ dYe«      Z…dZe†d&egfd[„Z‡dZe†d&egdz  fd\„Zˆ e!epege`f   «      Z‰eeed]<   d^e`d&eege`f   dz  fd_„ZŠd`e`d&elfda„Z‹ G db„ dce%«      ZŒdddeœdfegdKeJdge�dz  d&eŽegdhf   fdi„Z�dZe†dKeJd&e„fdj„Z�dke„d&e…fdl„Z‘dmee„   d&e…fdn„Z’dke„doe“eg   d&egdz  fdp„Z”dmee„   doe“eg   d&eŽegdhf   fdq„Z• G dr„ dse%«      Z– G dt„ due%«      Z—d7egdvegdKeJd&e5dz  fdw„Z˜dfegdxe“eg   dyeŽegdhf   d&eŽegdhf   fdz„Z™d{ee†   dxe“eg   dKeJd&e“eg   fd|„Zšd{ee†   dvegdKeJd&ecfd}„Z›d~eœd&ecfd„Z�d€eneg   d&ene†   fd�„Zžd7egd‚eneg   dƒeneg   d&dfd„„ZŸd…Z eeed†<   d‡e`d&elfdˆ„Z¡d7egd‰eld&e�fdŠ„Z¢dKeJd7egd&dfd‹„Z£d‡eege`f   dz  d&eldz  fdŒ„Z¤d;e0d&eldz  fd�„Z¥�dd7egdvegd&e5dz  fdŽ„Z¦d�e/d&eneg   fd�„Z§d‘efd’„Z¨�dd“egd&ee©   fd”„Zªd&ee«   fd•„Z¬ed�j[                  d–d— ee>«       ee¨«      g¬˜«      ed�j[                  d™d—dš ee>«       ee¨«      g¬›«      dœefd�„«       «       Z®ed�j[                  džd— ee>«       ee¨«      g¬˜«      dœefdŸ„«       Z¯ed�j[                  d d— ee>«       ee¨«      g¬˜«       edhd¡¬¢«      fdœed£egfd¤„«       Z°ed�j[                  d¥d— ee>«       ee¨«      g¬˜«      dœefd¦„«       Z±ed�j[                  d§d— ee>«       ee¨«      g¬˜«       edhd¨¬¢«      fdœed©egfdª„«       Z²ed�j[                  d«e³d— ee>«       ee¨«      g¬¬«      dœefd­„«       Z´d®egd&eŽegegf   dz  fd¯„Zµed�j[                  d°e¶d— ee>«       ee¨«      g¬¬«       ed±d±¬²«       ed³d±d´¬µ«       ed«      fd¶e�d·e�d¸egdz  fd¹„«       Z·ed�j[                  dºejd— ee>«       ee¨«      g¬¬«       edhd»¬¢«      fd7egfd¼„«       Z¸ed�js                  d°ejd½ ee>«       ee¨«      g¬¬«       edh«      fd;ejfd¾„«       Zºed�jw                  dºejd— ee>«       ee¨«      g¬¬«       edhd»¬¢«       edh«      fd7egd;ejfd¿„«       Z¼ed�j{                  dºdÀ ee>«      g¬˜«       edhd»¬¢«      fd7egfdÁ„«       Z¾dke`d&e†dz  fdÂ„Z¿dfe`d&eŽe†dhf   fdÃ„ZÀdfe`d&eneg   fdÄ„ZÁdÅegdz  dÆegd&eneg   fdÇ„ZÂdÈegdfe`dÉepege`f   d&dfdÊ„ZÃdÈegdfe`dÉepege`f   d&dfdË„ZÄdÈegdfe`d‡epege`f   d&dfdÌ„ZÅdÅegdÈegdfe`dÍepege`f   d&df
dÎ„ZÆdÈegdfe`dÏeÇeg   dÅegdz  d&eÇeg   dz  f
dÐ„ZÈdÅegd&egfdÑ„ZÉdÅegdÈegdfe`d‡epege`f   d&df
dÒ„ZÊdÅegdÈegdfe`d‡epege`f   d&df
dÓ„ZËdÔe0dÕeÌd&eŽepege`f   eÇeg   f   fdÖ„ZÍd×ed&elfdØ„ZÎ edÙdÙ¬Ú«       G dÛ„ dÜ«      «       ZÏd×eÐd&e�fdÝ„ZÑ G dÞ„ dßeÐ«      ZÒdàegdáeg ee`   f   d&eŽeÏdhf   fdâ„ZÓdãeeŽegeg ee`   f   f      d&eŽeÏdhf   fdä„ZÔd7egd9egd&dfdå„ZÕd7egd9egd&dfdæ„ZÖ	 �dd7egdçeneg   dèeneg   déeld&elf
dê„Z×ed�j±                  dºejd— ee>«       ee¨«      g¬¬«       edhd»¬¢«       edh«      fd7egdÕeÌfdë„«       ZÙ G dì„ díe%dš¬î«      ZÚed�j[                  dïe¶d— ee>«       ee¨«      g¬¬«       ed±d±¬²«       ed³d±d´¬µ«       ed«      fd¶e�d·e�d¸egdz  fdð„«       ZÛed�j[                  dñeœd— ee>«       ee¨«      g¬¬«       edhdò¬¢«      fdóegfdô„«       ZÜd9egdõegdz  döee2   d&e3fd÷„ZÝed�js                  dïeœd½ ee>«       ee¨«      g¬¬«       edh«      fd~eœfdø„«       ZÞed�jw                  dñeœd— ee>«       ee¨«      g¬¬«       edhdò¬¢«       edh«      fdóegd~eœfdù„«       Zßed�j{                  dñdÀ ee>«      g¬˜«       edhdò¬¢«      fdóegfdú„«       ZàdÕeÌdûe/dKeJd&eŽepege`f   eÇeg   eÇeg   dz  f   fdü„ZádóegdÉepege`f   dKeJfdý„ZâdóegdþeÇeg   dÿeÇeg   d&df�d „Zãed�j±                  dñeœd— ee>«       ee¨«      g¬¬«       edhdò¬¢«       edh«      fdóegdÕeÌf�d„«       Zäy(  uŒ   
âœ¨ SCIM v2 Endpoints for LiteLLM Proxy using Internal User/Team Management

This is an enterprise feature and requires a premium license.
é    N)Ú	AwaitableÚCallableÚIterableÚMappingÚSequence)Údeepcopy)Ú	dataclass)Úpartial)Úchain)ÚTYPE_CHECKINGÚFinalÚ
NamedTupleÚProtocolÚoverload)Ú	APIRouterÚBodyÚDependsÚHTTPExceptionÚPathÚQueryÚRequestÚResponse)Ú	BaseModelÚTypeAdapterÚValidationError)ÚReadOnlyÚ	TypedDictÚassert_never)Úverbose_proxy_logger)Úuuid©Ú
safe_dumps)ÚLiteLLM_TeamTableÚLiteLLM_UserTableÚLitellmUserRolesÚMemberÚNewTeamRequestÚNewUserRequestÚNewUserResponseÚProxyErrorTypesÚProxyExceptionÚTeamMemberAddRequestÚTeamMemberDeleteRequestÚUserAPIKeyAuth)Ú_delete_cache_key_object)Úuser_api_key_auth)Ú_safe_get_request_headers©Únew_user)ÚScimTransformations)Únew_teamÚteam_member_addÚteam_member_delete)ÚPrismaClientÚ_premium_user_checkÚhandle_exception_on_proxy)ÚInvitationLinkRepositoryÚ OrganizationMembershipRepositoryÚTeamMembershipRepository)ÚTeamRepository)ÚUserRepository)ÚVerificationTokenRepository)Ú*)ÚLiteLLM_VerificationTokenc                   ó  — e Zd Zdeeef   dedz  fd„Zdeeef   dedz  fd„Z	 	 	 	 ddeeef   dz  de	dz  de	dz  deeef   dz  de
e   f
d	„Zdeeef   d
eeef   defd„Zdeeef   dedz  fd„Zddeeef   dz  de	fd„Zy)Ú_UserTableClientÚwhereÚreturnNc              ƒ   ó   K  — y ­w©N© ©ÚselfrE   s     út/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/management_endpoints/scim/scim_v2.pyÚ
find_firstz_UserTableClient.find_firstP   ó   è ø� ùó   ‚c              ƒ   ó   K  — y ­wrH   rI   rJ   s     rL   Úfind_uniquez_UserTableClient.find_uniqueR   rN   rO   ÚskipÚtakeÚorderc              ƒ   ó   K  — y ­wrH   rI   ©rK   rE   rR   rS   rT   s        rL   Ú	find_manyz_UserTableClient.find_manyT   ó   è ø€ ð '*ùrO   Údatac              ƒ   ó   K  — y ­wrH   rI   ©rK   rE   rY   s      rL   Úupdatez_UserTableClient.update\   rN   rO   c              ƒ   ó   K  — y ­wrH   rI   rJ   s     rL   Údeletez_UserTableClient.delete^   rN   rO   c              ƒ   ó   K  — y ­wrH   rI   rJ   s     rL   Úcountz_UserTableClient.count`   rN   rO   ©NNNNrH   )Ú__name__Ú
__module__Ú__qualname__r   ÚstrÚobjectr$   rM   rQ   Úintr   rW   r\   r^   r`   rI   ó    rL   rD   rD   O   s  „ Ø\ g¨c°6¨kÑ&:Ð\Ð?PÐSWÑ?WÓ\à] w¨s°F¨{Ñ';Ð]Ð@QÐTXÑ@XÓ]ð .2ØØØ*.ñ*à�s˜F�{Ñ# dÑ*ð*ð �D‰jð*ð �D‰jð	*ð
 �s˜C�xÑ  4Ñ'ð*ð 
Ð#Ñ	$ó*ð n '¨#¨v¨+Ñ"6Ðm¸gÀcÈ6ÀkÑ>RÐmÐWhÓmàX '¨#¨v¨+Ñ"6ÐXÐ;LÈtÑ;SÓXáP ¨¨f¨Ñ!5¸Ñ!<ÐPÈÔPrh   rD   c                   óî   — e Zd Zdeeef   dedz  fd„Z	 	 	 	 ddeeef   dz  dedz  dedz  deeef   dz  de	e   f
d„Z
deeef   d	eeef   defd
„Zdeeef   dedz  fd„Zddeeef   dz  defd„Zy)Ú_TeamTableClientrE   rF   Nc              ƒ   ó   K  — y ­wrH   rI   rJ   s     rL   rQ   z_TeamTableClient.find_uniqued   rN   rO   rR   rS   rT   c              ƒ   ó   K  — y ­wrH   rI   rV   s        rL   rW   z_TeamTableClient.find_manyf   rX   rO   rY   c              ƒ   ó   K  — y ­wrH   rI   r[   s      rL   r\   z_TeamTableClient.updaten   rN   rO   c              ƒ   ó   K  — y ­wrH   rI   rJ   s     rL   r^   z_TeamTableClient.deletep   rN   rO   c              ƒ   ó   K  — y ­wrH   rI   rJ   s     rL   r`   z_TeamTableClient.countr   rN   rO   ra   rH   )rb   rc   rd   r   re   rf   r#   rQ   rg   r   rW   r\   r^   r`   rI   rh   rL   rj   rj   c   së   „ Ø] w¨s°F¨{Ñ';Ð]Ð@QÐTXÑ@XÓ]ð .2ØØØ*.ñ*à�s˜F�{Ñ# dÑ*ð*ð �D‰jð*ð �D‰jð	*ð
 �s˜C�xÑ  4Ñ'ð*ð 
Ð#Ñ	$ó*ð n '¨#¨v¨+Ñ"6Ðm¸gÀcÈ6ÀkÑ>RÐmÐWhÓmàX '¨#¨v¨+Ñ"6ÐXÐ;LÈtÑ;SÓXáP ¨¨f¨Ñ!5¸Ñ!<ÐPÈÔPrh   rj   c                   óV   — e Zd Zd	deeef   dz  ddfd„Zdeeef   deeef   ddfd„Zy)
Ú_VerificationTokenTableClientNrE   rF   z!Sequence[PrismaVerificationToken]c              ƒ   ó   K  — y ­wrH   rI   rJ   s     rL   rW   z'_VerificationTokenTableClient.find_manyv   rN   rO   rY   zPrismaVerificationToken | Nonec              ƒ   ó   K  — y ­wrH   rI   r[   s      rL   r\   z$_VerificationTokenTableClient.updatex   s	   è ø€ à+.ùrO   rH   )rb   rc   rd   r   re   rf   rW   r\   rI   rh   rL   rq   rq   u   sM   „ Ùt W¨S°&¨[Ñ%9¸DÑ%@ÐtÐLoÓtð/Ø˜S &˜[Ñ)ð/Ø18¸¸f¸Ñ1Eð/à	)ô/rh   rq   c                   ó&   — e Zd Zdeeef   defd„Zy)Ú_UserReferencingTableClientrE   rF   c              ƒ   ó   K  — y ­wrH   rI   rJ   s     rL   Údelete_manyz'_UserReferencingTableClient.delete_many~   rN   rO   N)rb   rc   rd   r   re   rf   rg   rw   rI   rh   rL   ru   ru   }   s   „ ØH w¨s°F¨{Ñ';ÐHÀÔHrh   ru   Ú
repositoryrF   c                  ó   — y rH   rI   ©rx   s    rL   Ú_tabler{   �   ó   € Ø<?rh   c                  ó   — y rH   rI   rz   s    rL   r{   r{   …   r|   rh   c                  ó   — y rH   rI   rz   s    rL   r{   r{   ‰   s   € ØVYrh   c                  ó   — y rH   rI   rz   s    rL   r{   r{   �   s   € ð #&rh   c                 ó   — | j                   S rH   )Útablerz   s    rL   r{   r{   “   s   € ð ×ÑÐrh   c                   óB   — e Zd ZdZe	 ddedededz  dedz  fd„«       Z	y)	ÚUserProvisionerHelpersz0Helper methods for user provisioning operations.NÚprisma_clientÚnew_user_requestÚadmin_grouprF   c           	   ƒ   ó`  K  — |j                   syt        t        | «      «      j                  d|j                   i¬«      ƒ d{  –—† }|syt	        t
        j                  |j                  xs g «      «      }|r|nt	        |j                  xs g «      }|j                  |j                  k7  r6t        j                  d|j                   |j                  |j                  «       t        |j                  |j                  xs g |¬«      ƒ d{  –—†  t        t        | «      «      j                  d|j                  i|j                   |j                  |t        |j                  «      dœ|�d|j                   ini ¥¬	«      ƒ d{  –—† }t#        j$                  |«      ƒ d{  –—† S 7 �Œb7 Œ›7 Œ&7 Œ­w)
a  
        Check if a user with the given email already exists and update them if found.

        The matched row keeps its existing user_id even when the SCIM userName differs.
        Virtual keys, team rosters, team/organization memberships and spend logs all
        reference that id, so re-keying the user row would strand every one of them and
        make removals against rosters holding the old id no-op. SCIM ids are opaque to
        the client, which reads the stable id back from the response.

        When admin_group is configured the resolved global role on new_user_request
        is persisted too, so re-upserting an existing email demotes a user who is no
        longer in the admin group instead of leaving the stale role.

        IdPs like Entra manage membership exclusively through /Groups and never send
        ``groups`` on POST /Users, so a request without teams means "unspecified",
        not "remove from every team": existing memberships are preserved then.

        Args:
            prisma_client: Database client
            new_user_request: New user request data
            admin_group: Configured SCIM admin group, or None to leave role untouched

        Returns:
            SCIMUser if user was updated, None if no existing user found
        NÚ
user_email©rE   z\SCIM: email %s already provisioned as user_id=%s, keeping that id instead of re-keying to %s©Úuser_idÚexisting_teamsÚ	new_teamsr‹   )rˆ   Ú
user_aliasÚteamsÚmetadataÚ	user_role©rE   rY   )rˆ   r{   r?   rM   ÚlistÚdictÚfromkeysr�   r‹   r   ÚinfoÚ_handle_team_membership_changesr\   rŽ   r"   r�   r‘   r4   Ú#transform_litellm_user_to_scim_user)r„   r…   r†   Úexisting_userÚrequested_teamsr�   Úupdated_users          rL   Úhandle_existing_user_by_emailz4UserProvisionerHelpers.handle_existing_user_by_email¡   s¤  è ø€ ð>  ×*Ò*Øä%+¬N¸=Ó,IÓ%J×%UÑ%UØÐ!1×!<Ñ!<Ð=ð &Vó &
÷  
ˆñ Øä!%¤d§m¡mÐ4D×4JÑ4JÒ4PÈbÓ&QÓ!RˆÙ.=™?Ä4È×H[ÑH[ÒHaÐ_aÓCbˆ	à×#Ñ# }×'<Ñ'<Ò<Ü ×%Ñ%ØnØ ×+Ñ+Ø×%Ñ%Ø ×(Ñ(ô	ô .Ø!×)Ñ)Ø(×.Ñ.Ò4°"Øô
÷ 	
ð 	
ô %+¬>¸-Ó+HÓ$I×$PÑ$PØ˜m×3Ñ3Ð4à.×9Ñ9Ø.×9Ñ9Ø"Ü&Ð'7×'@Ñ'@ÓAñ	ð
 ALÐ@W�KÐ!1×!;Ñ!;Ñ<Ð]_ðð %Qó 	%
÷ 	
ˆô )×LÑLÈ\ÓZ×ZÐZðG 
ùð$	
øð	
øð [úsJ   ‚AF.ÁF%ÁCF.ÄF(ÄA6F.ÆF*ÆF.Æ F,Æ!F.Æ(F.Æ*F.Æ,F.rH   )
rb   rc   rd   Ú__doc__Ústaticmethodr8   r(   re   ÚSCIMUserrœ   rI   rh   rL   rƒ   rƒ   ž   sT   „ Ù:àð #'ñD[Ø#ðD[à(ðD[ð ˜4‘ZðD[ð 
�D‰ò	D[ó ñD[rh   rƒ   c                   ó¾   — e Zd ZU dZedz  ed<   edz  ed<   edz  ed<   ee   ed<   edz  ed<   edz  ed<   edz  ed	<   edz  ed
<   ee	   dz  ed<   ee	   dz  ed<   y)ÚScimUserDataz-Typed structure for extracted SCIM user data.Nrˆ   rŽ   Ússo_user_idr�   Ú
given_nameÚfamily_nameÚactiveÚ
enterpriseÚentitlementsÚroles)
rb   rc   rd   r�   re   Ú__annotations__r“   ÚboolÚSCIMEnterpriseUserÚSCIMMultiValuedAttributerI   rh   rL   r¡   r¡   é   sx   … Ù7à�d‘
ÓØ�d‘
ÓØ�t‘ÓØ�‰9ÓØ�d‘
ÓØ�t‘ÓØ�4‰KÓØ" TÑ)Ó)ØÐ/Ñ0°4Ñ7Ó7ØÐ(Ñ)¨DÑ0Ô0rh   r¡   c                   óB   — e Zd ZU dZee   ed<   ee   ed<   ee   ed<   y)ÚGroupMemberExtractionResultzÞResult of extracting and processing group members.

    ``all_member_ids`` is deduped order-preserving; ``existing_member_ids`` is not,
    so a repeated resolved id appears once in the former and twice in the latter.
    Úexisting_member_idsÚcreated_usersÚall_member_idsN)rb   rc   rd   r�   r“   re   r©   r)   rI   rh   rL   r®   r®   ø   s(   … ñð ˜c™Ó"Ø˜Ñ(Ó(Ø˜‘IÔrh   r®   ú/scim/v2u   âœ¨ SCIM v2 (Enterprise Only))ÚprefixÚtagsÚdependenciesÚscim_routerc               ƒ   ó<   K  — ddl m}  | €t        dddi¬«      ‚| S ­w)z>Check if database is connected and raise HTTPException if not.r   ©r„   éô  ÚerrorzNo database connected©Ústatus_codeÚdetail)Úlitellm.proxy.proxy_serverr„   r   r¸   s    rL   Ú%_get_prisma_client_or_raise_exceptionr¿     s(   è ø€ å8àÐÜ¨°WÐ>UÐ4VÔWÐWØÐùs   ‚r‹   c              ƒ   ó¾   K  — t        «       ƒ d{  –—† }t        t        |«      «      j                  d| i¬«      ƒ d{  –—† }|st	        ddd| › �i¬«      ‚|S 7 ŒH7 Œ­w)z=Check if user exists and return user, raise 404 if not found.Nr‹   r‰   é”  rº   zUser not found with ID: r»   )r¿   r{   r?   rQ   r   )r‹   r„   Úusers      rL   Ú_check_user_existsrÃ     sf   è ø€ ä!FÓ!H×H€Mäœ~¨mÓ<Ó=×IÑIÐQZÐ\cÐPdÐIÓe×e€DáÜ¨°WÐ@XÐY`ÐXaÐ>bÐ4cÔdÐdà€Kð Iøàeúó   ‚A�A‘-A¾A¿AÁAÚteam_idc              ƒ   ó¾   K  — t        «       ƒ d{  –—† }t        t        |«      «      j                  d| i¬«      ƒ d{  –—† }|st	        ddd| › �i¬«      ‚|S 7 ŒH7 Œ­w)z=Check if team exists and return team, raise 404 if not found.NrÅ   r‰   rÁ   rº   úGroup not found with ID: r»   )r¿   r{   r>   rQ   r   )rÅ   r„   Úteams      rL   Ú_check_team_existsrÉ   !  sf   è ø€ ä!FÓ!H×H€Mäœ~¨mÓ<Ó=×IÑIÐQZÐ\cÐPdÐIÓe×e€DáÜ¨°WÐ@YÐZaÐYbÐ>cÐ4dÔeÐeà€Kð IøàeúrÄ   rÂ   c                 óf  — d}| j                   r1t        | j                   «      dkD  r| j                   d   j                  }d}| j                  r,| j                  j                  r| j                  j                  }g }| j
                  r#| j
                  D �cg c]  }|j                  ‘Œ }}||| j                  || j                  r| j                  j                  nd| j                  r| j                  j                  nd| j                  | j                  | j                  | j                  dœ
S c c}w )z)Extract common data from SCIMUser object.Nr   )
rˆ   rŽ   r¢   r�   r£   r¤   r¥   r¦   r§   r¨   )ÚemailsÚlenÚvalueÚnameÚ	givenNameÚgroupsÚ
externalIdÚ
familyNamer¥   Úenterprise_userr§   r¨   )rÂ   rˆ   rŽ   r�   Úgroups        rL   Ú_extract_scim_user_datarÕ   -  sê   € à€JØ‡{‚{”s˜4Ÿ;™;Ó'¨!Ò+Ø—[‘[ ‘^×)Ñ)ˆ
à€JØ‡y‚y�T—Y‘Y×(Ò(Ø—Y‘Y×(Ñ(ˆ
à€EØ‡{‚{Ø*.¯+©+Ö6 �—“Ð6ˆÐ6ð !Ø Ø—‘ØØ-1¯YªY�d—i‘i×)Ò)¸DØ/3¯yªy�t—y‘y×+Ò+¸dØ—+‘+Ø×*Ñ*Ø×)Ñ)Ø—‘ñð ùò 7s   ÂD.r£   r¤   r¥   r¦   r§   r¨   c                 ó>  — dt        | |¬«      j                  «       i}|�||d<   |�|j                  dd¬«      |t        <   |�'|D �cg c]  }|j                  d¬«      ‘Œ c}|t        <   |�'|D �cg c]  }|j                  d¬«      ‘Œ c}|t        <   |S c c}w c c}w )z)Build metadata dictionary with SCIM data.Úscim_metadata)rÏ   rÒ   Úscim_activeT)Úby_aliasÚexclude_none©rÚ   )ÚLiteLLM_UserScimMetadataÚ
model_dumpÚSCIM_ENTERPRISE_METADATA_KEYÚSCIM_ENTITLEMENTS_METADATA_KEYÚSCIM_ROLES_METADATA_KEY)	r£   r¤   r¥   r¦   r§   r¨   r�   ÚeÚrs	            rL   Ú_build_scim_metadatarã   I  s»   € ð 	Ô1Ø Ø"ô
÷ ‰*‹,ð	*€Hð ÐØ"(ˆ�ÑàÐØ1;×1FÑ1FÐPTÐcgÐ1FÓ1hˆÔ-Ñ.àÐØ]iÖ3jÐXY°A·L±LÈd°LÕ4SÒ3jˆÔ/Ñ0àÐØV[Ö,\ÐQR¨Q¯\©\Àt¨\Õ-LÒ,\ˆÔ(Ñ)à€Oùò 4kùò -]s   ÁBÁ0Bc               ƒ   ó
  K  — 	 ddl m}  | j                  «       ƒ d{  –—† }|j                  di «      xs i }|j                  dd«      }t	        |«      S 7 Œ7# t
        $ r }t        j                  d|«       Y d}~yd}~ww xY w­w)zî
    Get the scim_upsert_user setting from litellm_settings.

    Returns:
        True if scim_upsert_user is not set or is True (default behavior),
        False if scim_upsert_user is explicitly set to False (SCIM 2.0 strict mode)
    r   ©Úproxy_configNÚlitellm_settingsÚscim_upsert_userTz>Error reading scim_upsert_user setting, defaulting to True: %s)r¾   ræ   Ú
get_configÚgetrª   Ú	Exceptionr   Úwarning)ræ   Úconfigrç   rè   rá   s        rL   Ú_get_scim_upsert_user_settingrî   h  sƒ   è ø€ ðÝ;à*×5Ñ5Ó7×7ˆØ"(§*¡*Ð-?ÀÓ"DÒ"JÈÐØ"2×"6Ñ"6Ð7IÈ4Ó"PÐô Ð$Ó%Ð%ð 8ùô ò Ü×$Ñ$Ð%eÐghÔiäûðüs>   ‚B„A �Až6A ÁBÁA Á	B Á A;Á6BÁ;B Â Bc                  óˆ   — t         j                  r#t         j                  j                  d«      } | �| S t        j                  S )z2Non-admin default role for SCIM-provisioned users.r‘   )ÚlitellmÚdefault_internal_user_paramsrê   r%   ÚINTERNAL_USER_VIEW_ONLY)Úconfigured_roles    rL   Ú_default_scim_user_rolerô   ‡  s9   € ä×+Ò+Ü!(×!EÑ!E×!IÑ!IÈ+Ó!VˆØÐ&Ø"Ð"Ü×3Ñ3Ð3rh   c               ƒ   óú   K  — 	 ddl m}  | j                  «       ƒ d{  –—† }|j                  di «      xs i }|j                  d«      xs dS 7 Œ/# t        $ r }t        j                  d|«       Y d}~yd}~ww xY w­w)zÌ
    Get the scim_admin_group setting from litellm_settings.

    Returns the configured admin group identifier, or None when unset so callers
    leave a user's global role untouched (default-safe).
    r   rå   Nrç   Úscim_admin_groupz>Error reading scim_admin_group setting, defaulting to None: %s)r¾   ræ   ré   rê   rë   r   rì   )ræ   rí   rç   rá   s       rL   Ú_get_scim_admin_groupr÷   �  sx   è ø€ ðÝ;à*×5Ñ5Ó7×7ˆØ"(§*¡*Ð-?ÀÓ"DÒ"JÈÐØ×#Ñ#Ð$6Ó7Ò?¸4Ð?ð 8ùô ò Ü×$Ñ$Ð%eÐghÔiÜûðüs>   ‚A;„A �Až.A ÁA;ÁA Á	A8ÁA3Á.A;Á3A8Á8A;rÐ   r†   Údefault_rolec                 óz   — |€y| D ]2  }|j                   |k(  s|j                  |k(  sŒ"t        j                  c S  |S )a3  
    Resolve a user's global proxy role from their SCIM groups.

    Returns None when no admin group is configured, signalling callers to leave
    the role unchanged. Otherwise grants PROXY_ADMIN when any group matches the
    admin group by value or display, and falls back to the non-admin default.
    N)rÍ   Údisplayr%   ÚPROXY_ADMIN)rÐ   r†   rø   rÔ   s       rL   Ú_resolve_scim_user_rolerü   ¢  sG   € ð ÐØØò 0ˆØ�;‰;˜+Ò%¨¯©¸+Ó)EÜ#×/Ñ/Ò/ð0ð Ðrh   r„   Úteam_idsc              ƒ   ó
  K  — |D �cg c]0  }t        t        | «      «      j                  d|i¬«      ƒ d{  –—† ‘Œ2 }}t        ||«      D ��cg c]   \  }}t	        ||�|j
                  nd¬«      ‘Œ" c}}S 7 Œ@c c}w c c}}w ­w)zâ
    Build SCIMUserGroup objects from team ids, populating display from each
    team's alias so admin-group matching by display name works the same way it
    does on PUT (where SCIM groups carry display names natively).
    rÅ   r‰   N)rÍ   rú   )r{   r>   rQ   ÚzipÚSCIMUserGroupÚ
team_alias)r„   rý   rÅ   r�   rÈ   s        rL   Ú_scim_groups_from_team_idsr  ¶  s˜   è ø€ ð ltöØ`gŒf”^ MÓ2Ó3×?Ñ?ÀyÐRYÐFZÐ?Ó[×[Ñ[ð€Eð ô ! ¨5Ó1÷ñ
 ˆG�Tô	 	ØØ'+Ð'7�D—O’O¸Tö	
óð ð 	\úòùóùs1   ‚B‡.A8µA6
¶A8¼BÁ%A=Á2BÁ6A8Á8BÚuser_idsc              ƒ   ó†  K  — t        «       ƒ d{  –—† }|€yt        «       }|D ]‘  }t        t        | «      «      j	                  d|i¬«      ƒ d{  –—† }|€Œ4t        t        | |j                  xs g «      ƒ d{  –—† ||«      }t        t        | «      «      j                  d|id|i¬«      ƒ d{  –—†  Œ“ y7 Œ¨7 Œj7 ŒB7 Œ­w)aA  
    Recompute and persist each user's global proxy role from their resulting team
    membership. No-op unless scim_admin_group is configured, so a SCIM group write
    that drops a member from the admin group demotes them just like the user
    endpoints do, and the role is left untouched when the feature is off.
    Nr‹   r‰   r‘   r’   )	r÷   rô   r{   r?   rQ   rü   r  r�   r\   )r„   r  r†   rø   r‹   rÂ   Úresolved_roles          rL   Ú_recompute_scim_member_rolesr  È  sÙ   è ø€ ô  5Ó6×6€KØÐØä1Ó3€LØò 
ˆÜœN¨=Ó9Ó:×FÑFÈiÐY`ÐMaÐFÓb×bˆØˆ<ØÜ/Ü,¨]¸D¿J¹JÒ<LÈ"ÓM×MØØó
ˆô
 ”^ MÓ2Ó3×:Ñ:Ø˜gÐ&Ø˜}Ð-ð ;ó 
÷ 	
ñ 	
ñ
ð 7øð cøð Nøð	
úsE   ‚C�B9‘?CÁB;Á)CÁ:B=
Á;6CÂ1B?Â2CÂ;CÂ=CÂ?Cc                   ó   — e Zd ZU eed<   y)Ú_ResolvedUserMemberr‹   N©rb   rc   rd   re   r©   rI   rh   rL   r  r  ã  s   … Ø„Lrh   r  c                   ó(   — e Zd ZU eed<   ed   ed<   y)Ú_SkippedGroupMemberrÍ   )Únested_groupÚnon_user_typeÚexisting_teamÚreasonN)rb   rc   rd   re   r©   ÚLiteralrI   rh   rL   r  r  ç  s   … ØƒJØÐDÑEÔErh   r  c                   ó   — e Zd ZU eed<   y)Ú_UnknownMemberrÍ   Nr	  rI   rh   rL   r  r  ì  ó   … Ø„Jrh   r  c                   ó   — e Zd ZU eed<   y)Ú_AmbiguousMemberrÍ   Nr	  rI   rh   rL   r  r  ð  r  rh   r  c                   ó^   — e Zd ZU eedf   ed<   eedf   ed<   eedf   ed<   eedf   ed<   y)Ú_PartitionedMembers.Úresolved_idsÚskippedÚunknown_idsÚambiguous_valuesN)rb   rc   rd   Útuplere   r©   r  rI   rh   rL   r  r  ÷  s=   … Ø˜˜S˜‘/Ó!ØÐ&¨Ð+Ñ,Ó,Ø�s˜C�x‘Ó Ø˜C ˜H‘oÔ%rh   r  Úmemberc                 ó„   — | j                   r| j                   j                  «       st        dddi¬«      ‚| j                   S )zPA member id is opaque to us but has to be there; an empty one is a client error.é�  rº   z(Invalid member: user ID cannot be empty.r»   )rÍ   Ústripr   )r  s    rL   Ú_member_valuer!  þ  s=   € à�<Š<˜vŸ|™|×1Ñ1Ô3ÜØØÐGÐHô
ð 	
ð �<‰<Ðrh   c                 óf   — | j                   xs dj                  «       j                  «       }|xs dS )zQThe canonical ``type`` a member declares, lowercased; blank or absent means none.Ú N)Útyper   Úlower)r  Ú
normalizeds     rL   Ú_normalized_member_typer'    s-   € àŸ™Ò*¨×1Ñ1Ó3×9Ñ9Ó;€JØÒ˜Ðrh   Ú_JSON_OBJECT_ADAPTERÚrawc                 óL   — 	 t         j                  | «      S # t        $ r Y yw xY w)zEA typed, read-only view of a JSON object, or None when it is not one.N)r(  Úvalidate_pythonr   )r)  s    rL   Ú_json_object_fieldsr,    s*   € ðÜ#×3Ñ3°CÓ8Ð8øÜò Ùðús   ‚ —	#¢#Úteam_metadatac                 óŒ   — t        | «      }|€yt        |j                  t        «      «      xs |j                  t        «      duS )a-  Whether a group write from the identity provider left its mark on this team.

    ``SCIM_TEAM_DATA_METADATA_KEY`` counts because PUT has been writing it since
    long before the explicit marker, so a team the identity provider already
    syncs is recognized without waiting to be written again.
    NF)r,  rª   rê   ÚSCIM_MANAGED_TEAM_METADATA_KEYÚSCIM_TEAM_DATA_METADATA_KEY)r-  Úfieldss     rL   Ú"_team_metadata_has_scim_provenancer2    s@   € ô (¨Ó6€FØ€~ØÜ�—
‘
Ô9Ó:Ó;Òr¸v¿z¹zÔJeÓ?fÐnrÐ?rÐrrh   c                   ó.   — e Zd ZU ee   ed<   ee   ed<   y)Ú_CaseInsensitiveMatchÚequalsÚmodeN©rb   rc   rd   r   re   r©   rI   rh   rL   r4  r4  &  s   … Ø�S‰MÓØ
�3‰-Ôrh   r4  é   ©rS   rÍ   rS   .c             ƒ   óì   K  — | j                  «       }|ddœ}t        t        |«      «      j                  dd|id|igi|¬«      ƒ d{  –—† }t	        t
        j                  d„ |D «       «      «      S 7 Œ)­w)	aÝ  Every user id this member value names, by SSO identity or by email.

    Both fields are searched in one pass, because searching either first would hide a
    value that names one account by its SSO identity and another by its email, and
    hand the group to whichever field was searched first.

    They are not compared alike. An email is matched the way ``new_user`` matches one
    before it accepts a new account, case-insensitively: matching more strictly than
    the layer that would reject the placeholder is what turned a member id whose
    casing differed from the stored email into a 500 on the whole push. An SSO
    identity is matched exactly, because OIDC defines ``sub`` as case-sensitive and
    nothing folds its case on the way in, so treating two subjects that differ in case
    as one would hand the group to an account the provider never named.

    ``take`` bounds the read for a caller that only needs to know whether the value
    names one account or several; ``user_email`` carries no index, so letting the scan
    stop early is worth the two rows. A caller that has to know *which* accounts, as a
    removal does, passes None. That set is the accounts sharing one identity, which is
    a handful at worst.
    Úinsensitive)r5  r6  ÚORr¢   rˆ   )rE   rS   Nc              3   ó4   K  — | ]  }|j                   –— Œ y ­wrH   ©r‹   )Ú.0Úrows     rL   ú	<genexpr>z/_users_named_by_member_value.<locals>.<genexpr>I  s   è ø€ Ò;¨s˜sŸ{�{Ñ;ùó   ‚)r   r{   r?   rW   r  r”   r•   )rÍ   r„   rS   ÚsubjectÚemailÚrowss         rL   Ú_users_named_by_member_valuerF  +  s}   è ø€ ð. —[‘[“]€GØ5<ÀmÑ*T€EÜœ~¨mÓ<Ó=×GÑGà�} gÐ.°¸uÐ0EÐFÐGØð Hó ÷ €Dô
 ”—‘Ñ;°dÔ;Ó;Ó<Ð<ðús   ‚AA4ÁA2Á	*A4c              ƒ   ób  ‡K  — t        | «      Št        | «      }|dk(  rt        ‰d¬«      S t        t	        |«      «      j                  d‰i¬«      ƒ d{  –—† }|�Zt        ˆfd„t        ‰|«      ƒ d{  –—† D «       «      }|r&t        j                  d‰|d	   «       t        ‰¬
«      S t        ‰¬«      S |�|dk7  rt        ‰d¬«      S |€Rt        t        |«      «      j                  d‰i¬«      ƒ d{  –—† }|�"t        |j                  «      rt        ‰d¬«      S t        ‰|«      ƒ d{  –—† }t        |«      dk(  r)t        j                   d‰|d	   «       t        |d	   ¬«      S t        |«      dkD  r"t        j                  d‰«       t        ‰¬
«      S t#        ‰¬
«      S 7 �ŒP7 �Œ47 Œµ7 Œ­w)ar  
    Decide what a single SCIM group member refers to.

    A LiteLLM team only holds users, so a member is dropped when it declares a type
    other than ``User`` or when its id names an existing team. Both of those checks
    are placed around the user lookup rather than before it, because the id of a
    real user is the one thing that outranks them:

    - ``"type": "Group"`` (what Entra sends for a nested group) is dropped without
      a lookup. This bug provisioned nested group GUIDs as users, so those rows
      exist in the wild and would otherwise resolve as members all over again.
    - any other unrecognized type is dropped only after the user lookup misses.
      Clients do send non-canonical types on real members (RFC 7643 defines
      ``direct`` for ``User.groups``), and dropping a live user over one would
      revoke that user's team access on the next full sync.
    - an id that names an existing team is dropped only when the member arrives
      untyped, which is how Okta sends nested groups, and only when that team is
      one the identity provider writes. An id the IdP called a User is a user
      even if some team happens to share the id, and a team created here rather
      than through SCIM is not evidence of anything about the member.

    When those checks miss on an otherwise user-shaped member, its value is looked
    up as an SSO identity or an email, and a match resolves to that user's
    ``user_id``. A value that names more than one account is ambiguous rather than
    unknown: it names a real person we cannot identify, so it is neither guessed at
    nor provisioned.

    An exact ``user_id`` hit is checked the same way rather than trusted outright. A
    value can be one account's id and another's SSO identity or email, and taking the
    id on sight would hand the group to whichever account happened to be keyed by it.
    The placeholders this bug provisioned are that shape exactly, since they are keyed
    by the very id the provider keeps pushing, so on a tenant that already has them
    the membership is refused and named rather than silently landing on the
    placeholder again.
    rÔ   r  )rÍ   r  r‹   r‰   Nc              3   ó.   •K  — | ]  }|‰k7  sŒ	|–— Œ y ­wrH   rI   )r?  ÚotherrÍ   s     €rL   rA  z)_classify_group_member.<locals>.<genexpr>x  s   øè ø€ ò #
ØÐ[`ÐdiÓ[iŒEñ#
ùs   ƒ
Ža  SCIM: group member '%s' is one account's user id and is also account '%s' by SSO identity or email, so the membership cannot be attributed. A placeholder an earlier release provisioned under this id looks exactly like this and should be deleted so the real account can be matchedr   ©rÍ   r>  rÂ   r  rÅ   r  é   zESCIM: group member '%s' matched user_id '%s' by SSO identity or emailzqSCIM: group member '%s' names more than one account by SSO identity or email and cannot be resolved unambiguously)r!  r'  r  r{   r?   rQ   r  rF  r   rì   r  r  r>   r2  r�   rÌ   r–   r  )r  r„   Úmember_typerÂ   Úshared_withrÈ   ÚnamedrÍ   s          @rL   Ú_classify_group_memberrO  L  s¿  øè ø€ ôH ! Ó(€EÜ0°Ó8€Kà�gÒÜ"¨°~ÔFÐFäœ~¨mÓ<Ó=×IÑIÐQZÐ\aÐPbÐIÓc×c€DØÐÜ"ó #
Ü%AÀ%ÈÓ%W×Wô#
ó 
ˆñ Ü ×(Ñ(ðcð Ø˜A‘ôô $¨%Ô0Ð0Ü"¨5Ô1Ð1àÐ ;°&Ò#8Ü"¨°ÔGÐGàÐÜ"¤>°-Ó#@ÓA×MÑMÐU^Ð`eÐTfÐMÓg×gˆØÐÔ BÀ4Ç=Á=Ô QÜ&¨U¸?ÔKÐKä5°e¸]ÓK×K€EÜ
ˆ5ƒz�Q‚Ü×!Ñ!ØSØØ�!‰Hô	
ô
 #¨5°©8Ô4Ð4Ü
ˆ5ƒz�A‚~Ü×$Ñ$ðàô	
ô
   eÔ,Ð,ä Ô&Ð&ðQ dùð  Xùð" høð LúsJ   ƒAF/ÁF%ÁF/Á4F(
Á5B F/Ã5F+Ã67F/Ä-F-Ä.A8F/Æ(F/Æ+F/Æ-F/Úentryc                 ó  — | xt         d˜ x�\  } t        |fddd¬«      S  xt        d˜ x�\    t        d| fdd¬«      S  xt        d˜ x�\  } t        dd|fd¬«      S  t        d˜ x�\  }t        ddd|f¬«      S  	 t        | «       y)z=The single-member partition one classified entry contributes.r>  NrI   ©r  r  r  r  rJ  )r  r  r  r  r  r   )rP  r‹   rÍ   s      rL   Ú_bucketed_memberrS  ¡  sš   € à
Ø1Ô ×1Ü&°W°JÈÐXZÐmoÔpÐpð 2à"Ô Ö"Ü&°BÀÀÐVXÐkmÔnÐnð #à(Œ^×(Ü&°BÀÐQVÐPXÐkmÔnÐnð )äÖ*Ü&°BÀÐPRÐfkÐemÔnÐnð +àÜ˜Õrh   Ú
classifiedc                 ó\  — t        d„ | D «       «      }t        t        t        j                  d„ |D «       «      «      t        t        j                  d„ |D «       «      «      t        t        j                  d„ |D «       «      «      t        t        j                  d„ |D «       «      «      ¬«      S )zVSplit classified members into the buckets the resolver acts on, keeping request order.c              3   ó2   K  — | ]  }t        |«      –— Œ y ­wrH   )rS  ©r?  rP  s     rL   rA  z0_partition_classified_members.<locals>.<genexpr>²  s   è ø€ ÒL¸Ô,¨U×3ÑLùó   ‚c              3   ó4   K  — | ]  }|j                   –— Œ y ­wrH   )r  ©r?  Úbuckets     rL   rA  z0_partition_classified_members.<locals>.<genexpr>´  s   è ø€ Ò.ZÀv¨v×/BÕ/BÑ.ZùrB  c              3   ó4   K  — | ]  }|j                   –— Œ y ­wrH   )r  rZ  s     rL   rA  z0_partition_classified_members.<locals>.<genexpr>µ  s   è ø€ Ò)P¸V¨&¯.­.Ñ)PùrB  c              3   ó4   K  — | ]  }|j                   –— Œ y ­wrH   )r  rZ  s     rL   rA  z0_partition_classified_members.<locals>.<genexpr>¶  s   è ø€ Ò-XÀV¨f×.@Õ.@Ñ-XùrB  c              3   ó4   K  — | ]  }|j                   –— Œ y ­wrH   )r  rZ  s     rL   rA  z0_partition_classified_members.<locals>.<genexpr>·  s   è ø€ Ò2bÈv°6×3JÕ3JÑ2bùrB  rR  )r  r  r   Úfrom_iterable)rT  Úbucketeds     rL   Ú_partition_classified_membersra  °  s…   € äÑLÀÔLÓL€HÜÜœ5×.Ñ.Ñ.ZÐQYÔ.ZÓZÓ[Ü”e×)Ñ)Ñ)PÀxÔ)PÓPÓQÜœ%×-Ñ-Ñ-XÈxÔ-XÓXÓYÜœu×2Ñ2Ñ2bÐYaÔ2bÓbÓcô	ð rh   Úcreated_idsc                 ó¬   — | xt         d˜ x�\  } |S  xt        d˜ x�\  } ||v r|S d S  xt        d˜ x�\    y  xt        d˜ x�\    y   	 t	        | «       y )Nr>  rJ  rI   )r  r  r  r  r   )rP  rb  r‹   rÍ   s       rL   Ú_admitted_member_idrd  »  sm   € Ø
Ø1Ô ×1ØˆNð 2à(Œ^×(Ø! [Ñ0�5Ð:°dÐ:ð )à"Ô Õ"Ð7Øð #Ð%7Ô%5Õ%7Ð7Øð &8Ð7àÜ˜Õrh   c                 óV   ‡‡— t        t        j                  ˆˆfd„| D «       «      «      S )a>  Member ids that survive resolution, in the order the request listed them.

    An id the request repeats is one member: the roster these ids are written to
    holds one row per member, and a second creation attempt for the same id fails
    against the real unique constraint even though the first one succeeded.
    c              3   ó@   •K  — | ]  }t        |‰«      xŠ€Œ‰–— Œ y ­wrH   )rd  )r?  rP  rb  Ú	member_ids     €€rL   rA  z'_admitted_member_ids.<locals>.<genexpr>Ï  s*   øè ø€ ò 
ØÔ?RÐSXÐZeÓ?fÐ2f°)Ñ1sŒIñ
ùs   ƒ—)r  r”   r•   )rT  rb  rg  s    `@rL   Ú_admitted_member_idsrh  Ç  s+   ù€ ô Ü�‰ô 
Ø#-ô
ó 	
óð rh   c                   ó   — e Zd ZU ee   ed<   y)Ú_UserIdWherer‹   Nr7  rI   rh   rL   rj  rj  Õ  s   … Ø�c‰]Ôrh   rj  c                   ó   — e Zd ZU ee   ed<   y)Ú_ScimErrorDetailrº   Nr7  rI   rh   rL   rl  rl  Ù  s   … Ø�C‰=Ôrh   rl  Úcreated_viac              ƒ   óÔ   K  — t        | |¬«      ƒ d{  –—† }|�|S d| i}t        t        |«      «      j                  |¬«      ƒ d{  –—† }|�ydd| › d�i}t	        d|¬	«      ‚7 ŒP7 Œ­w)
a‰  The created user, or None when the id already resolves to a user row (a
    concurrent provisioning request won the creation race after our lookup missed).

    Raises:
        HTTPException: 500 when the user can neither be created nor found. The
        request has to fail so the identity provider retries, instead of recording
        success for a member the roster silently dropped.
    )r‹   rm  Nr‹   r‰   rº   zFailed to create user 'z&' while provisioning group membership.r¹   r»   )Ú_create_user_if_not_existsr{   r?   rQ   r   )r‹   rm  r„   ÚcreatedrE   Úexistingr½   s          rL   Ú_ensure_group_member_userrr  Ý  sŠ   è ø€ ô 6¸gÐS^Ô_×_€GØÐØˆØ"+¨WÐ!5€EÜ"¤>°-Ó#@ÓA×MÑMÐTYÐMÓZ×Z€HØÐØàÐ*¨7¨)Ð3YÐZð'€Fô  C°Ô
7Ð7ð `øð [ús!   ‚A(“A$”3A(ÁA&ÁA(Á&A(ÚrosterÚresolvedc           	      ót   ‡— t        t        j                  t        | ‰v r| fndˆfd„|D «       «      «      «      S )aÙ  The members of this group a removal value names.

    Both ways of naming one count together. The id as written counts when the roster
    holds it verbatim, which is how an earlier release recorded a member it could not
    match, and the accounts it resolves to count when they are on the roster. Counting
    only the resolved ones would let a value that is one member's canonical id and
    another member's email revoke both, since each looks singular on its own.
    rI   c              3   ó,   •K  — | ]  }|‰v sŒ|–— Œ y ­wrH   rI   )r?  r‹   rs  s     €rL   rA  z+_roster_entries_named_by.<locals>.<genexpr>  s   øè ø€ ÒF˜W°G¸vÒ4E”ÑFùs   ƒ	�)r  r”   r•   r   )rÍ   rs  rt  s    ` rL   Ú_roster_entries_named_byrw  ÷  s;   ø€ ô Ü�‰ÜØ! V™O�‘°ÛF¨ÔFóó	
óð rh   Úmembersc              ƒ   ó`  K  — t        d„ | D «       «      }t        t        |«      D �cg c]%  }|t        ||t	        ||d¬«      ƒ d{  –—† «      f‘Œ' c}«      }t        d„ |D «       «      }|rt        ddd|d   › d	�i¬
«      ‚t        t        j                  d„ |D «       «      «      S 7 Œac c}w ­w)aA  The members a ``remove`` clears, one per id the request names.

    The roster holds canonical user ids, so a directory that added someone by their
    email or SSO identity has to be able to remove them by that same value, and a
    member an earlier release recorded under the raw id has to stay removable by it.

    Ambiguity is a property of the table as it stands, not of the value, so a value
    that named one person when they were admitted can name two later. Resolving a
    removal against the whole table would then drop nobody while answering 200, and
    the person the directory just took out of the group would keep the team. So a
    removal keeps only the accounts already on the roster: one is unambiguous however
    many strangers share the address, none means there is nothing to revoke, and only
    a value naming two of this group's own members is genuinely undecidable. That last
    case fails rather than reporting a removal it did not perform, or revoking both.

    Raises:
        HTTPException: 400 when a member id names more than one current member.
    c              3   ó2   K  — | ]  }t        |«      –— Œ y ­wrH   )r!  ©r?  r  s     rL   rA  z&_member_ids_to_drop.<locals>.<genexpr>  s   è ø€ ÒK¸œ}¨V×4ÑKùrX  Nr9  c              3   óD   K  — | ]  \  }}t        |«      d kD  sŒ|–— Œ y­w)rK  N)rÌ   )r?  rÍ   Úentriess      rL   rA  z&_member_ids_to_drop.<locals>.<genexpr>+  s    è ø€ ÒW©¨°ÄcÈ'ÃlÐUVÓFVœuÑWùs   ‚ ™ r  rº   úMember ID 'r   z˜' names more than one member of this group, so the removal cannot be attributed. Send the LiteLLM user ID as the member value, or resolve the duplicate.r»   c              3   ó&   K  — | ]	  \  }}|–— Œ y ­wrH   rI   )r?  Ú_r}  s      rL   rA  z&_member_ids_to_drop.<locals>.<genexpr>4  s   è ø€ Ò(K±Z°Q¸¬Ñ(Kùs   ‚)Ú	frozensetr  Úsortedrw  rF  r   r   r_  )rx  rs  r„   ÚwrittenrÍ   ÚmatchedÚundecidables          rL   Ú_member_ids_to_dropr†  
  sÏ   è ø€ ô* ÑKÀ7ÔKÓK€GÜô   ›ö	
ð ð Ü(Ø˜6Ô)EÀeÈ]ÐaeÔ)f×#fóòò	
ó
€Gô ÑW¸7ÔWÓW€KÙÜØà˜; {°1¡~Ð&6ð 7pð pðô
ð 	
ô ”U×(Ñ(Ñ(KÀ7Ô(KÓKÓLÐLð $gúò		
ùs)   ‚%B.§B)ÁB'ÁB)ÁAB.Â'B)Â)B.c              ƒ   óF  K  — t        | D �cg c]  }t        ||«      ƒ d{  –—† ‘Œ c}«      }t        |«      }|j                  D ]-  }t	        j
                  d|j                  |j                  «       Œ/ |j                  r t        ddd|j                  d   › d�i¬«      ‚|j                  r2t        «       ƒ d{  –—† s t        ddd	|j                  d   › d
�i¬«      ‚t        t        j                  |j                  «      «      }|D ]  }t	        j                  d|«       Œ t        |D �cg c]  }|t        |||¬«      ƒ d{  –—† f‘Œ c}«      }	t        d„ |	D «       «      }
t!        |j"                  |
t%        |t'        |«      «      ¬«      S 7 �Œpc c}w 7 ŒÝ7 ŒUc c}w ­w)aH  
    Resolve SCIM group members to LiteLLM user ids, dropping members that are not users.

    Member ids are matched by ``user_id`` first, then by SSO identity or email. An
    id that resolves to nothing is created when litellm_settings.scim_upsert_user is
    True (default) and rejected per SCIM 2.0 otherwise. Removals do not come through
    here: they resolve through ``_member_ids_to_drop`` instead, which neither creates
    a user nor fails on an id it cannot place.

    Raises:
        HTTPException: 400 when a member id is empty, when a member id names more
        than one user, or when scim_upsert_user is False and a member id is neither
        an existing user, an existing team, nor a member declared to be something
        other than a user. 500 when a member's user row can neither be created nor
        found.
    NzPSCIM: ignoring non-user group member '%s' (%s); LiteLLM teams contain users onlyr  rº   r~  r   zÊ' names more than one LiteLLM user, so the group membership cannot be attributed. Resolve the duplicate, which for an id that also matches a SCIM-provisioned placeholder means deleting that placeholder.r»   zUser with ID 'zV' does not exist. Please create the user first via POST /Users before adding to group.z¹SCIM: creating placeholder user for group member '%s'; matched no user by user_id, sso_user_id or user_email. An SSO-provisioned user's real account stays teamless if this is a mismatch)r‹   rm  r„   c              3   ó,   K  — | ]  \  }}|€Œ	|–— Œ y ­wrH   rI   )r?  r€  rp  s      rL   rA  z,_resolve_group_member_ids.<locals>.<genexpr>z  s   è ø€ Ò \©Z¨Q°ÈÑH[¤Ñ \ùs   ‚
�)r¯   r°   r±   )r  rO  ra  r  r   r–   rÍ   r  r  r   r  rî   r”   r•   rì   rr  r®   r  rh  r�  )rx  rm  r„   r  rT  Ú	partitionr  Úunique_unknown_idsr‹   Ú	creationsr°   s              rL   Ú_resolve_group_member_idsrŒ  7  sÏ  è ø€ ô* ÐahÖiÐW]Ô%;¸FÀMÓ%R×RÑRÒiÓj€JÜ4°ZÓ@€Ià×$Ñ$ò 
ˆÜ×!Ñ!Ø^Ø�M‰MØ�N‰Nõ	
ð
ð ×!Ò!ÜØà˜; y×'AÑ'AÀ!Ñ'DÐ&Eð FPð Pðô
ð 	
ð ×ÒÔ+HÓ+J×%JÐ%JÜØà˜>¨)×*?Ñ*?ÀÑ*BÐ)Cð DWð Wðô
ð 	
ô !&¤d§m¡m°I×4IÑ4IÓ&JÓ KÐØ%ò 
ˆÜ×$Ñ$ðfàõ	
ð
ô ð .ö	
ð
 ð Ü/¸È[ÐhuÔv×vòò	
ó€Iô !Ñ \¸9Ô \Ó\€Mä&Ø%×2Ñ2Ø#Ü+¨J¼	ÐBTÓ8UÓVôð ða  SûÒið( &Køð* wúò	
ùsX   ‚
F!ŒF F¡F§BF!Â:FÂ;A2F!Ä-FÅFÅFÅAF!ÆFÆF!ÆFÆF!rÔ   c              ƒ   ó~   K  — t        «       ƒ d{  –—† }t        | j                  xs g d|¬«      ƒ d{  –—† S 7 Œ(7 Œ­w)aù  
    Extract member IDs from SCIMGroup, validating that all users exist.

    Behavior depends on litellm_settings.scim_upsert_user:
    - If True (default): Creates users that don't exist (backward compatible)
    - If False: Rejects non-existent users per SCIM 2.0 protocol

    Returns:
        GroupMemberExtractionResult with existing members, created users, and all member IDs

    Raises:
        HTTPException: If scim_upsert_user is False and any member user does not exist (400 Bad Request)
    NÚscim_group_membership©rx  rm  r„   )r¿   rŒ  rx  )rÔ   r„   s     rL   Ú_extract_group_member_idsr�  ƒ  sD   è ø€ ô "GÓ!H×H€MÜ*Ø—‘Ò# Ø+Ø#ô÷ ð ð Iøðús   ‚=�9‘#=´;µ=»=Ú
member_idsc              ƒ   ó0  K  — t        «       ƒ d{  –—† }g }| D ]t  }t        t        |«      «      j                  d|i¬«      ƒ d{  –—† }|sŒ4|j                  xs |j
                  }|j                  t        |j
                  |d¬«      «       Œv |S 7 Œ�7 ŒN­w)zCGet SCIMMember objects with display names for a list of member IDs.Nr‹   r‰   ÚUser©rÍ   rú   r$  )r¿   r{   r?   rQ   rˆ   r‹   ÚappendÚ
SCIMMember)r‘  r„   rx  rg  rÂ   Údisplay_names         rL   Ú_get_team_members_displayr˜  ™  s�   è ø€ ä!FÓ!H×H€MØ')€Gàò ^ˆ	ÜœN¨=Ó9Ó:×FÑFÈiÐYbÐMcÐFÓd×dˆÚØŸ?™?Ò:¨d¯l©lˆLØ�N‰Nœ:¨D¯L©LÀ,ÐU[Ô\Õ]ð	^ð €Nð Iøð eús(   ‚B�B‘4BÁBÁBÁABÂBrŒ   r�   c              ƒ   ó¬   K  — t        |«      }t        |«      }||z
  }||z
  }|s|r*t        | t        |«      t        |«      d¬«      ƒ d{  –—†  yy7 Œ­w)zàHandle adding/removing user from teams based on changes.

    Roster write failures propagate so the SCIM endpoint returns an error the IdP
    retries, instead of persisting a ``teams`` array the roster never received.
    T©r‹   Úteams_ids_to_add_user_toÚteams_ids_to_remove_user_fromÚraise_on_errorN)ÚsetÚpatch_team_membershipr“   )r‹   rŒ   r�   Úexisting_teams_setÚnew_teams_setÚteams_to_addÚteams_to_removes          rL   r—   r—   §  sh   è ø€ ô !$ NÓ 3ÐÜ˜y›>€Mà'Ð*<Ñ<€LØ/°-Ñ?€Oá‘Ü#ØÜ%)¨,Ó%7Ü*.¨Ó*?Øô	
÷ 	
ñ 	
ð 'ð	
ús   ‚A	AÁAÁAÚscim_blockedÚSCIM_BLOCKED_METADATA_KEYr�   c                 óT   — t        | t        «      xr | j                  t        «      du S )zGTrue if a verification token carries the SCIM-block marker in metadata.T)Ú
isinstancer”   rê   r¥  )r�   s    rL   Ú_key_was_scim_blockedr¨  Ã  s$   € ä�h¤Ó%ÒY¨(¯,©,Ô7PÓ*QÐUYÐ*YÐYrh   Úblockedc              ƒ   ó°  K  — ddl m}m} t        «       ƒ d{  –—† }|r8t	        t        |«      «      j                  | ddiddigdœ¬«      ƒ d{  –—† }|}nTt	        t        |«      «      j                  | dd	œ¬«      ƒ d{  –—† }|D �cg c]  }t        |j                  «      sŒ|‘Œ }}|sy|D ]´  }t        |j                  t        «      rt        |j                  «      ni }	|ri |	¥t        di¥}
n-|	j                  «       D ��ci c]  \  }}|t        k7  sŒ||“Œ }
}}t	        t        |«      «      j                  d
|j                  i|t        |
«      dœ¬«      ƒ d{  –—†  Œ¶ |D ]"  }t!        |j                  ||¬«      ƒ d{  –—†  Œ$ t#        j$                  d|rdndt'        |«      | «       t'        |«      S 7 �Œ¦7 �Œr7 �ŒCc c}w c c}}w 7 Œq7 ŒL­w)a¤  
    Block or unblock virtual keys owned by a user and invalidate them in the
    in-memory/redis caches so the change takes effect immediately.

    Each key SCIM blocks is tagged with ``metadata.scim_blocked = True``. On
    reactivation we only unblock keys carrying that marker, so a key an admin
    blocked manually for unrelated reasons is left alone.

    Returns the number of keys whose state was flipped.
    r   )Úproxy_logging_objÚuser_api_key_cacheNr©  F)r‹   r<  r‰   T©r‹   r©  Útoken)r©  r�   r’   )Úhashed_tokenr¬  r«  z)SCIM: %s %d virtual key(s) for user_id=%sÚ	unblocked)r¾   r«  r¬  r¿   r{   r@   rW   r¨  r�   r§  r”   r¥  Úitemsr\   r®  r"   r/   r   r–   rÌ   )r‹   r©  r«  r¬  r„   Ú
candidatesÚaffected_keysÚkÚkey_rowÚcurrent_metadataÚnew_metadataÚvs               rL   Ú_set_user_keys_blockedr¹  È  sü  è ø€ ÷ Qä!FÓ!H×H€Máô "Ô"=¸mÓ"LÓM×WÑWà"Ø! 5Ð)¨I°tÐ+<Ð=ñð Xó 
÷ 
ˆ
ð #‰ä!Ô"=¸mÓ"LÓM×WÑWØ%°$Ñ7ð Xó 
÷ 
ˆ
ð %/ÖT˜qÔ2GÈÏ
É
Õ2SšÐTˆÐTáØà ò 	
ˆÜHRÐSZ×ScÑScÔeiÔHj¬d°7×3CÑ3CÔ.DÐprÐÙØPÐ.ÐPÔ0IÈ4ÑP‰Là-=×-CÑ-CÓ-E×h¡T Q¨ÈÔNgÓIg˜A˜q™DÐhˆLÑhÜÔ0°Ó?Ó@×GÑGØ˜GŸM™MÐ*Ø$´*¸\Ó2JÑKð Hó 
÷ 	
ñ 	
ð	
ð !ò 
ˆÜ&Ø Ÿ™Ø1Ø/ô
÷ 	
ñ 	
ð
ô ×ÑØ3Ù‰	 +ÜˆMÓØô	ô ˆ}ÓÐð] Iùð
ùð
ûò Uùó ið	
øð	
ús}   ‚G˜F>™6GÁGÁ1GÂGÂGÂ
GÂ%GÂ)AGÄGÄGÄAGÅ GÅ!&GÆGÆ7GÇGÇGÇGÇGc             ƒ   ó>  K  — t        t        | «      «      j                  dd|id|id|igi¬«      ƒ d{  –—†  t        t        | «      «      j                  d|i¬«      ƒ d{  –—†  t        t	        | «      «      j                  d|i¬«      ƒ d{  –—†  y7 Œa7 Œ57 Œ	­w)a   Drop rows whose foreign keys reference ``LiteLLM_UserTable.user_id``.

    Required before deleting the user row itself, otherwise Postgres rejects
    the user delete with an FK constraint violation (e.g.
    ``LiteLLM_InvitationLink_user_id_fkey``).
    r<  r‹   Ú
created_byÚ
updated_byr‰   N)r{   r;   rw   r<   r=   )r„   r‹   s     rL   Ú_delete_rows_referencing_userr½    s¹   è ø€ ô Ô)¨-Ó8Ó
9×
EÑ
EàØ˜GÐ$Ø˜wÐ'Ø˜wÐ'ðð
ð Fó ÷ ð ô Ô1°-Ó@Ó
A×
MÑ
MÐU^Ð`gÐThÐ
MÓ
i×iÐiÜ
Ô)¨-Ó8Ó
9×
EÑ
EÈYÐX_ÐL`Ð
EÓ
a×aÑaðøð jøØaús3   ‚3BµB¶-BÁ#BÁ$-BÂBÂBÂBÂBc                 óF   — | sy| j                  d«      }|€yt        |«      S )zBRead the SCIM active flag from a user's metadata dict, if present.NrØ   )rê   rª   )r�   rÍ   s     rL   Ú_scim_active_valuer¿    s(   € áØØ—<‘< Ó.€EØ€}ØÜ�‹;Ðrh   c                 ó0   — | j                   }t        |«      S )z@Read the SCIM active flag off a user row's metadata, if present.)r�   r¿  )rÂ   r�   s     rL   Ú_user_scim_activerÁ  $  s   € à04·±€HÜ˜hÓ'Ð'rh   c           	   ƒ   óp  K  — ddl m} 	 t        j                  }t        j
                  rt        j
                  j                  d«      }t        | | dg d|id|¬«      } ||¬«      ƒ d{  –—† }t        j                  d	| |«       |S 7 Œ# t        $ r!}t        j                  d
| |«       Y d}~yd}~ww xY w­w)a  
    Helper function to create a user if they don't exist.

    Args:
        user_id: The user ID to create
        created_via: Context for where the user was created from

    Returns:
        LiteLLM_UserTable if user was created, None if creation failed
    r   r2   r‘   Nrm  F©r‹   rˆ   rŽ   r�   r�   Úauto_create_keyr‘   ©rY   zCreated user %s via %szFailed to create user %s: %s)Ú:litellm.proxy.management_endpoints.internal_user_endpointsr3   r%   rò   rð   rñ   rê   r(   r   r–   rë   Ú	exception)r‹   rm  r3   rø   r…   Úcreated_userrá   s          rL   ro  ro  *  s¾   è ø€ õ Tðô ×4Ñ4ð 	ô ×/Ò/Ü"×?Ñ?×CÑCÀKÓPˆLä"0ØØØØØ# [Ð1Ø!Ø"ô#
Ðñ %-Ð2BÔ$C×CˆÜ×!Ñ!Ð":¸GÀ[ÔQØÐð Dùô ò Ü×&Ñ&Ð'EÀwÐPQÔRÜûðüsA   ‚B6ŠAB	 Á)BÁ*B	 ÂB6ÂB	 Â		B3ÂB.Â)B6Â.B3Â3B6rÈ   c              ƒ   ó  K  — g }| j                   xs g D ]q  }t        |d«      r(|j                  �|j                  |j                  «       Œ7t	        |t
        «      sŒHd|v sŒM|j                  d«      }|€Œa|j                  |«       Œs |S ­w)z{
    Get the IDs of the members from a team.

    Use one source of truth for the member IDs: team.members_with_roles

    r‹   )Úmembers_with_rolesÚhasattrr‹   r•  r§  r”   rê   )rÈ   Úmember_user_idsr  r‹   s       rL   Ú#_get_team_member_user_ids_from_teamrÍ  X  s‡   è ø€ ð )+€OØ×)Ñ)Ò/¨Rò 0ˆÜ�6˜9Ô%¨&¯.©.Ð*DØ×"Ñ" 6§>¡>Õ2Ü˜¤Õ%¨)°vÒ*=Ø—j‘j Ó+ˆGØÑ"Ø×&Ñ& wÕ/ð0ð Ðùs   ‚ABÁBÁ"BÁ6BÚresponsec              ƒ   ó*   K  — d| j                   d<   y­w)z5Sets the Content-Type header to application/scim+jsonzapplication/scim+jsonzContent-TypeN)Úheaders)rÎ  s    rL   Úset_scim_content_typerÑ  k  s   è ø€ ð (?€H×Ñ�^Ò$ùs   ‚Úbase_urlc                 ó^   — t        ddddd| › d�ddœ¬«      t        d	d	d	d
d| › d�ddœ¬«      gS )zHReturn the list of SCIM ResourceType definitions per RFC 7643 Section 6.r“  úUser Accountú/Usersú*urn:ietf:params:scim:schemas:core:2.0:Userz/ResourceTypes/UserÚResourceType©ÚlocationÚresourceType)ÚidrÎ   ÚdescriptionÚendpointÚschema_ÚmetaÚGroupú/Groupsú+urn:ietf:params:scim:schemas:core:2.0:Groupz/ResourceTypes/Group)ÚSCIMResourceType)rÒ  s    rL   Ú_get_resource_typesrä  r  sd   € ô 	ØØØ&ØØ@à'˜jÐ(;Ð<Ø .ñô
	
ô 	ØØØØØAà'˜jÐ(<Ð=Ø .ñô
	
ðð rh   c                  ót  — t        dddt        dddddd	d
d¬«      t        dddddt        ddd¬«      t        ddd¬«      t        ddd¬«      g¬«      t        dddd¬«      t        ddddt        ddd¬«      t        ddd ¬«      t        d!d"d#¬«      g¬$«      t        d%d"dd&¬«      t        d'ddd(d)t        ddd*¬«      t        d+dd,¬«      g¬-«      t        d.ddd/t        ddd0¬«      t        d+dd1¬«      t        ddd2¬«      t        d!d"d3¬«      g¬$«      t        d4ddd5t        ddd6¬«      t        d+dd7¬«      t        ddd8¬«      t        d!d"d9¬«      g¬$«      gd:d;d<œ¬=«      t        d>d?d?t        dddd@dd	d
dA¬«      t        dBdddCt        dddD¬«      t        d+ddE¬«      t        dddF¬«      g¬$«      gdGd;d<œ¬=«      gS )HzBReturn the list of SCIM Schema definitions per RFC 7643 Section 7.rÖ  r“  rÔ  ÚuserNameÚstringFzUnique identifier for the User.TÚ	readWriteÚdefaultÚserver)rÎ   r$  ÚmultiValuedrÜ  ÚrequiredÚ
mutabilityÚreturnedÚ
uniquenessrÎ   Úcomplexz'The components of the user's real name.rÏ   zThe given name of the User.)rÎ   r$  rÜ  rÒ   zThe family name of the User.Ú	formattedzThe full name.)rÎ   r$  rë  rÜ  rì  ÚsubAttributesÚdisplayNamez+The name of the User, suitable for display.)rÎ   r$  rë  rÜ  rË   zEmail addresses for the user.rÍ   zEmail address value.r$  z!Type of email (work, home, etc.).ÚprimaryÚbooleanz"Whether this is the primary email.)rÎ   r$  rë  rÜ  rò  r¥   z#Whether the user account is active.rÐ   z!Groups to which the user belongs.ÚreadOnlyzGroup identifier.rú   zGroup display name.)rÎ   r$  rë  rÜ  rí  rò  r§   z$A list of entitlements for the user.zThe value of an entitlement.z*A human-readable name for the entitlement.z.A label indicating the entitlement's function.z(Whether this is the primary entitlement.r¨   zA list of roles for the user.zThe value of a role.z#A human-readable name for the role.z'A label indicating the role's function.z!Whether this is the primary role.z;/scim/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:UserÚSchemarØ  )rÛ  rÎ   rÜ  Ú
attributesrß  râ  rà  z$A human-readable name for the Group.Únonerx  zA list of members of the Group.zMember identifier.zMember display name.z…The type of member; canonical values are "User" and "Group". Only members of type User are honored, LiteLLM teams contain users only.z</scim/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:Group)Ú
SCIMSchemaÚSCIMSchemaAttributerI   rh   rL   Ú_get_schemasrü  Ž  s×  € ô 	Ø;ØØ&ä#Ø#Ø!Ø %Ø AØ!Ø*Ø&Ø'ô	ô $ØØ"Ø %Ø IØ"ä+Ø!,Ø!)Ø(Eôô
 ,Ø!-Ø!)Ø(Fôô
 ,Ø!,Ø!)Ø(8ôð#ôô0 $Ø&Ø!Ø %Ø Mô	ô $Ø!Ø"Ø $Ø ?ä+Ø!(Ø!)Ø(>ôô
 ,Ø!'Ø!)Ø(Kôô
 ,Ø!*Ø!*Ø(Lôð#ôô. $Ø!Ø"Ø %Ø Eô	ô $Ø!Ø"Ø $Ø CØ)ä+Ø!(Ø!)Ø(;ôô
 ,Ø!*Ø!)Ø(=ôð#ôô& $Ø'Ø"Ø $Ø Fä+Ø!(Ø!)Ø(Fôô
 ,Ø!*Ø!)Ø(Tôô
 ,Ø!'Ø!)Ø(Xôô
 ,Ø!*Ø!*Ø(Rôð!#ôô8 $Ø Ø"Ø $Ø ?ä+Ø!(Ø!)Ø(>ôô
 ,Ø!*Ø!)Ø(Môô
 ,Ø!'Ø!)Ø(Qôô
 ,Ø!*Ø!*Ø(Kôð!#ôðkQðf ZØ (ñômZ	
ôv 	Ø<ØØä#Ø&Ø!Ø %Ø FØ!Ø*Ø&Ø%ô	ô $Ø"Ø"Ø $Ø Aä+Ø!(Ø!)Ø(<ôô
 ,Ø!*Ø!)Ø(>ôô
 ,Ø!'Ø!)ð!kô	ð#ôð%ðN [Ø (ñôU.	
ðyKð Krh   r#  éÈ   )r¼   rµ   ú/F)r¼   Úinclude_in_schemarµ   Úrequestc              ƒ   ó&  K  — t        j                  d| j                  | j                  «       t	        | j
                  «      j                  d«      dz   }t        |«      }dgt        |«      |D �cg c]  }|j                  «       ‘Œ c}dœS c c}w ­w)zü
    Base SCIM v2 endpoint for resource discovery per RFC 7644 Section 4.

    Returns a ListResponse of ResourceTypes supported by this SCIM service provider.
    Identity providers (Okta, Azure AD, etc.) use this endpoint for resource discovery.
    z6SCIM base resource discovery request: method=%s url=%srþ  r²   ú2urn:ietf:params:scim:api:messages:2.0:ListResponse©ÚschemasÚtotalResultsÚ	Resources©
r   ÚdebugÚmethodÚurlre   rÒ  Úrstriprä  rÌ   rÝ   ©r   rÒ  Úresource_typesÚrts       rL   Úget_scim_baser  ^  s…   è ø€ ô$ ×ÑØ@Ø�‰Ø�‰ôô
 ˜'×*Ñ*Ó+×2Ñ2°3Ó7¸*ÑD€HÜ/°Ó9€NàHÐIÜ˜NÓ+Ø0>Ö?¨"�b—m‘m•oÒ?ñð ùò @ùó   ‚A.BÁ0BÂ
Bz/ResourceTypesc              ƒ   ó&  K  — t        j                  d| j                  | j                  «       t	        | j
                  «      j                  d«      dz   }t        |«      }dgt        |«      |D �cg c]  }|j                  «       ‘Œ c}dœS c c}w ­w)z“
    SCIM ResourceTypes endpoint per RFC 7644 Section 4.

    Returns a ListResponse of all resource types supported by this service provider.
    z,SCIM ResourceTypes request: method=%s url=%srþ  r²   r  r  r  r  s       rL   Úget_resource_typesr  ~  s…   è ø€ ô ×ÑØ6Ø�‰Ø�‰ôô
 ˜'×*Ñ*Ó+×2Ñ2°3Ó7¸*ÑD€HÜ/°Ó9€NàHÐIÜ˜NÓ+Ø0>Ö?¨"�b—m‘m•oÒ?ñð ùò @ùr  z!/ResourceTypes/{resource_type_id}zResourceType ID)ÚtitleÚresource_type_idc              ƒ   ó  K  — t        j                  d|«       t        | j                  «      j	                  d«      dz   }t        |«      }|D ]#  }|j                  |k(  sŒ|j                  «       c S  t        ddd|› �i¬«      ‚­w)z7
    Get a single ResourceType by ID per RFC 7644.
    z#SCIM ResourceType request for id=%srþ  r²   rÁ   rº   zResourceType not found: r»   )	r   r  re   rÒ  r  rä  rÛ  rÝ   r   )r   r  rÒ  r  r  s        rL   Úget_resource_typer  —  s�   è ø€ ô ×ÑÐDÐFVÔWÜ˜'×*Ñ*Ó+×2Ñ2°3Ó7¸*ÑD€HÜ/°Ó9€NØò #ˆØ�5‰5Ð$Ó$Ø—=‘=“?Ò"ð#ô ØØÐ3Ð4DÐ3EÐFÐGôð ùs   ‚ABÁ %Bz/Schemasc              ƒ   óÖ   K  — t        j                  d| j                  | j                  «       t	        «       }dgt        |«      |D �cg c]  }|j                  «       ‘Œ c}dœS c c}w ­w)z†
    SCIM Schemas endpoint per RFC 7643 Section 7.

    Returns a ListResponse of all schemas supported by this service provider.
    z&SCIM Schemas request: method=%s url=%sr  r  )r   r  r	  r
  rü  rÌ   rÝ   )r   r  Úss      rL   Úget_schemasr  ¯  s`   è ø€ ô ×ÑØ0Ø�‰Ø�‰ôô
 "“^€GàHÐIÜ˜G›Ø.5Ö6¨�a—l‘l•nÒ6ñð ùò 7ùs   ‚AA)ÁA$Á
A)z/Schemas/{schema_id:path}z
Schema URIÚ	schema_idc              ƒ   ó¾   K  — t        j                  d|«       t        «       }|D ]#  }|j                  |k(  sŒ|j	                  «       c S  t        ddd|› �i¬«      ‚­w)z@
    Get a single Schema by its URI per RFC 7643 Section 7.
    zSCIM Schema request for id=%srÁ   rº   zSchema not found: r»   )r   r  rü  rÛ  rÝ   r   )r   r  r  r  s       rL   Ú
get_schemar  Ç  si   è ø€ ô ×ÑÐ>À	ÔJÜ!“^€GØò "ˆØ�4‰4�9ÓØ—<‘<“>Ò!ð"ô ØØÐ-¨i¨[Ð9Ð:ôð ùs
   ‚5A¸%Az/ServiceProviderConfig)Úresponse_modelr¼   rµ   c              ƒ   ó¼   K  — t        j                  d| j                  | j                  t	        | «      «       dt        | j                  «      dœ}t        |¬«      S ­w)z+Return SCIM Service Provider Configuration.z?SCIM ServiceProviderConfig request: method=%s url=%s headers=%sÚServiceProviderConfig)rÚ  rÙ  )rß  )r   r  r	  r
  r1   re   ÚSCIMServiceProviderConfig)r   rß  s     rL   Úget_service_provider_configr!  Þ  sT   è ø€ ô ×ÑØIØ�‰Ø�‰Ü! 'Ó*ô	ð 0Ü˜Ÿ™Ó$ñ€Dô %¨$Ô/Ð/ùs   ‚AAÚscim_filterc                 ó´   — t        j                  d| t         j                  ¬«      }|sy|j                  d«      j	                  «       |j                  d«      fS )zHParse the SCIM equality filters Okta uses before user lifecycle changes.z%\s*([\w.]+)\s+eq\s+(['"]?)(.*?)\2\s*$©ÚflagsNrK  é   )ÚreÚmatchÚ
IGNORECASErÔ   r%  )r"  r(  s     rL   Ú_parse_scim_eq_filterr*  ó  sI   € ä—8‘8Ø4ØÜ�m‰mô€Eñ
 ØØ�;‰;�q‹>×ÑÓ! 5§;¡;¨q£>Ð1Ð1rh   rÕ  rK  )Úgeé
   éd   )r+  ÚleÚ
startIndexr`   Úfilterc           
   ƒ   ó~  K  — t        j                  d| ||«       	 t        «       ƒ d{  –—† }i }|r-t        |«      }|r |\  }}|dk(  rd|id|ig|d<   n
|dk(  r||d<   t	        t        |«      «      j                  || dz
  |d	d
i¬«      ƒ d{  –—† }t	        t        |«      «      j                  |¬«      ƒ d{  –—† }	g }
|D ]1  }t        j                  |¬«      ƒ d{  –—† }|
j                  |«       Œ3 t        |	| t        |t        |
«      «      |
¬«      S 7 Œï7 ŒŒ7 Œb7 Œ?# t        $ r}t        |«      ‚d}~ww xY w­w)z;
    Get a list of users according to SCIM v2 protocol
    z8SCIM GET USERS request: startIndex=%s count=%s filter=%sNÚusernamerˆ   r‹   r<  zemails.valuerK  Ú
created_atÚdesc©rE   rR   rS   rT   r‰   ©rÂ   ©r  r/  ÚitemsPerPager  )r   r  r¿   r*  r{   r?   rW   r`   r4   r˜   r•  ÚSCIMListResponseÚminrÌ   rë   r:   )r/  r`   r0  r„   Úwhere_conditionsÚparsed_filterÚfilter_attributeÚfilter_valueÚusersÚtotal_countÚ
scim_usersrÂ   Ú	scim_userrá   s                 rL   Ú	get_usersrC     s…  è ø€ ô ×ÑØBØØØô	ð,+Ü%JÓ%L×Lˆà57ÐÙô $9¸Ó#@ˆMÙØ1>Ñ.Ð  ,Ø# zÒ1à% |Ð4Ø" LÐ1ð.Ð$ TÒ*ð &¨Ò7Ø5AÐ$ \Ñ2ô ;AÄÐP]ÓA^Ó:_×:iÑ:iØ"Ø˜q‘.ØØ Ð(ð	 ;jó ;
÷ 5
ˆô $*¬.¸Ó*GÓ#H×#NÑ#NÐUeÐ#NÓ#f×fˆð -/ˆ
Øò 	)ˆDÜ1×UÑUÐ[_Ô`×`ˆIØ×Ñ˜iÕ(ð	)ô  Ø$Ø!Ü˜U¤C¨
£OÓ4Ø ô	
ð 	
ðG  Møð&5
øð gøð
 aùô ò +Ü'¨Ó*Ð*ûð+üso   ‚D=œD! ©DªA$D! ÂDÂ+D! Â:DÂ;$D! ÃDÃ 8D! ÄD=ÄD! ÄD! ÄD! ÄD! Ä!	D:Ä*D5Ä5D:Ä:D=z/Users/{user_id}zUser IDc              ƒ   óÜ   K  — t        j                  d| «       	 t        | «      ƒ d{  –—† }t        j                  |«      ƒ d{  –—† }|S 7 Œ#7 Œ# t
        $ r}t        |«      ‚d}~ww xY w­w)z?
    Get a single user by ID according to SCIM v2 protocol
    z$SCIM GET USER request for user_id=%sN)r   r  rÃ   r4   r˜   rë   r:   )r‹   rÂ   rB  rá   s       rL   Úget_userrE  C  so   è ø€ ô ×ÑÐEÀwÔOð+Ü.¨wÓ7×7ˆô "5×!XÑ!XÐY]Ó!^×^ˆ	ØÐð	 8øð _ùô ò +Ü'¨Ó*Ð*ûð+üsJ   ‚A,šA ¨A©A ÁAÁA ÁA,ÁA ÁA Á	A)ÁA$Á$A)Á)A,éÉ   c           	   ƒ   ó²  K  — 	 t        j                  d| j                  «       «       t        «       ƒ d{  –—† }t	        | «      }| j
                  rVt        t        |«      «      j                  d| j
                  i¬«      ƒ d{  –—† }|rt        ddd| j
                  › �i¬«      ‚| j
                  xs t        t        j                  «       «      }t        |d	   |d
   |d   |d   |d   ¬«      }t        «       }t        «       ƒ d{  –—† }t!        | j"                  xs g ||«      }t%        ||d   |d   |d   |d|�|n|¬«      }	t&        j)                  ||	|¬«      ƒ d{  –—† }
|
r|
S t+        |	¬«      ƒ d{  –—† }t-        j.                  |¬«      ƒ d{  –—† }|S 7 �Œj7 �Œ7 Œœ7 ŒD7 Œ.7 Œ# t        $ r}|‚d}~wt0        $ r}t3        |«      ‚d}~ww xY w­w)z5
    Create a user according to SCIM v2 protocol
    zSCIM CREATE USER request: %sNr‹   r‰   é™  rº   z#User already exists with username: r»   r£   r¤   r¦   r§   r¨   ©r¦   r§   r¨   rˆ   rŽ   r�   FrÃ  )r„   r…   r†   rÅ  r6  )r   r  rÝ   r¿   rÕ   ræ  r{   r?   rQ   r   re   r    Úuuid4rã   rô   r÷   rü   rÐ   r(   rƒ   rœ   r3   r4   r˜   rë   r:   )rÂ   r„   Ú	user_datar™   r‹   r�   rø   r†   r  r…   Úexisting_user_scimrÈ  rB  rá   s                 rL   Úcreate_userrM  [  s÷  è ø€ ð;+Ü×"Ñ"Ð#AÀ4Ç?Á?ÓCTÔUÜ%JÓ%L×Lˆô 3°4Ó8ˆ	ð �=Š=Ü"(¬¸Ó)FÓ"G×"SÑ"SÐ[dÐfj×fsÑfsÐZtÐ"SÓ"u×uˆMÙÜ#Ø #Ø#Ð'JÈ4Ï=É=È/Ð%ZÐ[ôð ð Ÿ™Ò;¬#¬d¯j©j«lÓ*;ˆÜ.Ø�lÑ#Ø�mÑ$Ø  Ñ.Ø" >Ñ2Ø˜GÑ$ô
ˆô 6Ó7ˆÜ#8Ó#:×:ˆÜ6°t·{±{Ò7HÀbÈ+ÐWcÓdˆä"0ØØ  Ñ.Ø  Ñ.Ø˜GÑ$ØØ!Ø'2Ð'>‘mÀLô#
Ðô +A×*^Ñ*^Ø'Ø-Ø#ð +_ó +
÷ %
Ðñ Ø%Ð%ä$,Ø!ô%
÷ 
ˆô "5×!XÑ!XÐ^jÔ!k×kˆ	ØÐðk  Mùð vùð$ ;øð%
øð
øð lùäò ØˆûÜò +Ü'¨Ó*Ð*ûð+üs­   ‚G„1F- µF¶AF- ÂF"ÂBF- ÄF%Ä	AF- Å"F'Å#F- Å*GÅ+F- Å:F)Å;F- ÆF+ÆF- ÆGÆF- Æ"F- Æ%F- Æ'F- Æ)F- Æ+F- Æ-	GÆ6F8Æ8GÇGÇGÇGc           	   ƒ   ó†  K  — t        j                  d| |j                  «       «       	 t        «       ƒ d{  –—† }t	        | «      ƒ d{  –—† }t        |«      }t        |«      }d|j                  v }|r|d   n|}t        |d   |d   ||d   |d   |d   ¬	«      }t        | |j                  xs g |d
   ¬«      ƒ d{  –—†  |d   |d   |d   |d
   t        |«      dœ}	t        «       ƒ d{  –—† }
|
�&t        |j                  xs g |
t        «       «      |	d<   t!        t#        |«      «      j%                  d| i|	¬«      ƒ d{  –—† }|r,t'        |«      }|�||€dn|k7  rt)        | | ¬«      ƒ d{  –—†  t+        j,                  |«      ƒ d{  –—† }|S 7 �Œ_7 �ŒO7 ŒÝ7 Œ°7 Œ[7 Œ/7 Œ# t.        $ r}t1        |«      ‚d}~ww xY w­w)zH
    Update a user according to SCIM v2 protocol (full replacement)
    z(SCIM PUT USER request for user_id=%s: %sNr¥   r£   r¤   r¦   r§   r¨   rI  r�   rŠ   rˆ   rŽ   r¢   )rˆ   rŽ   r¢   r�   r�   r‘   r‹   r’   Tr­  )r   r  rÝ   r¿   rÃ   rÁ  rÕ   Úmodel_fields_setrã   r—   r�   r"   r÷   rü   rÐ   rô   r{   r?   r\   r¿  r¹  r4   r˜   rë   r:   )r‹   rÂ   r„   r™   Úprev_activerK  Úclient_set_activeÚscim_active_for_metadatar�   Úupdate_datar†   r›   Ú
new_activerB  rá   s                  rL   Úupdate_userrU  ¥  s  è ø€ ô ×ÑØ2ØØ�‰Óôð=+Ü%JÓ%L×LˆÜ%7¸Ó%@×@ˆä.¨}Ó=ˆä2°4Ó8ˆ	ð $,¨t×/DÑ/DÐ#DÐÙAR¨)°HÒ*=ÐXcÐ ä.Ø�lÑ#Ø�mÑ$Ø$Ø  Ñ.Ø" >Ñ2Ø˜GÑ$ô
ˆô .ØØ(×.Ñ.Ò4°"Ø Ñ(ô
÷ 	
ð 	
ð $ LÑ1Ø# LÑ1Ø$ ]Ñ3Ø˜wÑ'Ü" 8Ó,ñ
ˆô $9Ó#:×:ˆØÐ"Ü'>Ø—‘Ò!˜r ;Ô0GÓ0Ió(ˆK˜Ñ$ô %+¬>¸-Ó+HÓ$I×$PÑ$PØ˜gÐ&Øð %Qó %
÷ 
ˆñ
 Ü 2°8Ó <ˆJØÐ%¨*ÀÐAT¹ÐZeÒ*fÜ,°WÈ*ÀnÔU×UÐUô "5×!XÑ!XÐYeÓ!f×fˆ	àÐðs  MùØ@ùð.	
øð ;øð
øð Vøð gùô ò +Ü'¨Ó*Ð*ûð+üs¦   ‚&G©F% ¶F·F% Á	FÁ
A3F% Â=FÂ>.F% Ã,FÃ-AF% ÅFÅ-F% Å1F!Å2F% ÆF#ÆF% ÆGÆF% ÆF% ÆF% ÆF% ÆF% Æ!F% Æ#F% Æ%	F>Æ.F9Æ9F>Æ>GéÌ   c           
   ƒ   óp  ‡ K  — t        j                  d‰ «       	 t        «       ƒ d{  –—† }t        ‰ «      ƒ d{  –—† }t	        |j
                  xs g D �cg c]0  }t        t        |«      «      j                  d|i¬«      ƒ d{  –—† ‘Œ2 c}«      }t	        d„ |D «       «      }|D ]î  }|j                  xs g }‰ |v rP|D �cg c]
  }|‰ k7  sŒ	|‘Œ }	}t        t        |«      «      j                  d|j                  id|	i¬«      ƒ d{  –—†  t        j                  |j                  «       «      }
t        ˆ fd„|
j                   xs g D «       «      sŒ­t#        t%        |
j                  ‰ ¬	«      t'        t(        j*                  ¬
«      ¬«      ƒ d{  –—†  Œð t-        ‰ d¬«      ƒ d{  –—†  t/        |‰ ¬«      ƒ d{  –—†  t        t1        |«      «      j3                  d‰ i¬«      ƒ d{  –—†  t5        d¬«      S 7 �ŒÔ7 �ŒÄ7 �Œ}c c}w c c}w 7 �Œ7 Œ€7 Œk7 ŒX7 Œ,# t6        $ r}t9        |«      ‚d}~ww xY w­w)z5
    Delete a user according to SCIM v2 protocol
    z'SCIM DELETE USER request for user_id=%sNrÅ   r‰   c              3   ó&   K  — | ]	  }|sŒ|–— Œ y ­wrH   rI   )r?  rÈ   s     rL   rA  zdelete_user.<locals>.<genexpr>  s   è ø€ ÒB dºTœTÑBùó   ‚Šrx  r’   c              3   ó<   •K  — | ]  }|j                   ‰k(  –— Œ y ­wrH   r>  )r?  r  r‹   s     €rL   rA  zdelete_user.<locals>.<genexpr>  s   øè ø€ Ò]°�6—>‘> WÕ,Ñ]ùs   ƒ©rÅ   r‹   ©r‘   ©rY   Úuser_api_key_dictTr­  r>  r‹   rV  ©r¼   )r   r  r¿   rÃ   r  r�   r{   r>   rQ   rx  r\   rÅ   r#   Úmodel_validaterÝ   ÚanyrÊ  r7   r-   r.   r%   rû   r¹  r½  r?   r^   r   rë   r:   )r‹   r„   r™   rÅ   Úfound_teamsr�   rÈ   Úcurrent_membersÚmÚnew_membersÚteam_rowrá   s   `           rL   Údelete_userrg  ø  s'  øè ø€ ô ×ÑÐHÈ'ÔRð&+Ü%JÓ%L×LˆÜ%7¸Ó%@×@ˆô #ð  -×2Ñ2Ò8°böàô œ^¨MÓ:Ó;×GÑGÈyÐZaÐNbÐGÓc×cÑcòó
ˆô ÑB¨kÔBÓBˆð ò 	ˆDØ-1¯\©\Ò-?¸RˆOØ˜/Ñ)Ø*9ÖJ Q¸QÀ'»\šqÐJ�ÐJÜœ^¨MÓ:Ó;×BÑBØ$ d§l¡lÐ3¸9ÀkÐ:Rð Có ÷ ð ô )×7Ñ7¸¿¹Ó8IÓJˆHÜÓ]¸8×;VÑ;VÒ;\ÐZ\Ô]Õ]Ü(Ü0¸×9IÑ9IÐSZÔ[Ü&4Ô?O×?[Ñ?[Ô&\ô÷ ñ ð	ô %¨W¸dÔC×CÐCä+¨MÀ7ÔK×KÐKô ”^ MÓ2Ó3×:Ñ:À)ÈWÐAUÐ:ÓV×VÐVä CÔ(Ð(ðG  MùØ@ùð
 dûòùò Kðùðøð
 	DøàKøð 	Wùô ò +Ü'¨Ó*Ð*ûð+üsÙ   ƒH6›H ¨G<©H »G?¼H Á.HÂHÂHÂ6H Ã
H
ÃH
Ã8H Ä	HÄ
A	H Å=H ÆHÆH Æ(HÆ)H Æ=HÆ>-H Ç+HÇ,H Ç;H6Ç<H Ç?H ÈHÈH ÈH ÈH ÈH ÈH È	H3È#H.È.H3È3H6c                 ó8  — t        | t        «      rt        | ¬«      S t        | «      }|€y|j	                  d«      }|sy|j	                  d«      }|j	                  d«      }t        t        |«      |�t        |«      ndt        |t        «      r|¬«      S d¬«      S )zEParse one entry of a SCIM patch value, or None when it carries no id.rJ  NrÍ   rú   r$  r”  )r§  re   r–  r,  rê   )rP  r1  Úentry_valueÚentry_displayÚ
entry_types        rL   Ú_parse_member_entryrl  -  s™   € ä�%œÔÜ Ô&Ð&ä'¨Ó.€FØ€~ØàŸ™ GÓ,€KÙØà!Ÿ:™: iÓ0€MØŸ
™
 6Ó*€JÜÜ�+ÓØ&3Ð&?”�MÔ"ÀTÜ% j´#Ô6ˆZôð ð =Aôð rh   c                 óp   — t        | t        «      rt        | «      n| f}t        d„ d„ |D «       D «       «      S )a  Parse a SCIM patch value into members, keeping each entry's ``type``.

    PATCH bodies bypass SCIMGroup parsing (SCIMPatchOperation.value is untyped),
    so member objects arrive as raw dicts and the ``type`` that marks a nested
    group would otherwise be lost.
    c              3   ó&   K  — | ]	  }|€Œ|–— Œ y ­wrH   rI   r{  s     rL   rA  z(_parse_member_entries.<locals>.<genexpr>K  s   è ø€ Òn˜FÐ[aÑ[m”ÑnùrY  c              3   ó2   K  — | ]  }t        |«      –— Œ y ­wrH   )rl  rW  s     rL   rA  z(_parse_member_entries.<locals>.<genexpr>K  s   è ø€ Ò&WÀeÔ':¸5×'AÑ&WùrX  )r§  r“   r  )rÍ   r}  s     rL   Ú_parse_member_entriesrp  C  s4   € ô :DÀEÌ4Ô9P¬¨u¬ÐW\ÐV^€GÜÑnÑ&WÈwÔ&WÔnÓnÐnrh   c                 óR   — t        | «      D �cg c]  }|j                  ‘Œ c}S c c}w )z)Return group ids from a SCIM patch value.)rp  rÍ   )rÍ   r  s     rL   Ú_extract_group_valuesrr  N  s   € ä'<¸UÓ'CÖD˜VˆF�L‹LÒDÐDùÒDs   Ž$ÚpathÚ	attributec                 óæ   — | sg S t        j                  dt        j                  |«      › d�| t         j                  ¬«      }|sg S t        j                  dd|j                  d«      «      }|r|gS g S )a™  Return ids from a SCIM filtered path like ``members[value eq "id"]``.

    Okta commonly sends membership removals as a filtered path and omits the
    request body ``value``, so the id lives only inside the ``[value eq "..."]``
    filter. The ``eq`` operator is matched case-insensitively per the SCIM
    spec; the id keeps its original case. Per the SCIM filter grammar the
    compared value must be quoted (single or double), so malformed unquoted
    filters yield no id. A quoted id may contain escaped quotes and
    backslashes (``\"`` and ``\\``), which are unescaped before use.
    ``path`` must be the raw, case-preserving path from the patch op.
    z\s*z7\s*\[\s*value\s+eq\s+(['"])((?:\\.|[^\\])*?)\1\s*\]\s*$r$  z\\(.)z\1r8  )r'  r(  Úescaper)  ÚsubrÔ   )rs  rt  r(  Ú	extracteds       rL   Ú_extract_ids_from_path_filterry  S  sn   € ñ Øˆ	Ü—8‘8Ø”—‘˜9Ó%Ð&Ð&]Ð`ØÜ�m‰mô€Eñ
 Øˆ	Ü—v‘v˜h¨¨u¯{©{¸1«~Ó>€IÙ#ˆIˆ;Ð+¨Ð+rh   Úop_typerS  c                 ó6   — | dk(  rd|d<   yt        |«      |d<   y)zHandle displayname updates.ÚremoveNrŽ   ©re   ©rz  rÍ   rS  s      rL   Ú_handle_displayname_updater  l  s"   € à�(ÒØ$(ˆ�LÒ!ä$'¨£Jˆ�LÒ!rh   c                 ó6   — | dk(  rd|d<   yt        |«      |d<   y)zHandle externalid updates.r|  Nr¢   r}  r~  s      rL   Ú_handle_externalid_updater�  t  s"   € à�(ÒØ%)ˆ�MÒ"ä%(¨£Zˆ�MÒ"rh   c                 ó    — | dk(  r|j                  dd«       y|}t        |t        «      r|j                  «       dk(  }nt	        |«      }||d<   y)zHandle active status updates.r|  rØ   NÚtrue)Úpopr§  re   r%  rª   )rz  rÍ   r�   Úbool_vals       rL   Ú_handle_active_updater†  |  sJ   € à�(ÒØ�‰�] DÕ)àˆÜ�eœSÔ!Ø—{‘{“}¨Ñ.‰Hä˜E“{ˆHØ"*ˆ�Òrh   r×   c                 ó´   — | dk(  r'|dk(  r|j                  dd«       yt        |«      |d<   y| dk(  r'|dk(  r|j                  dd«       yt        |«      |d<   yy)z2Handle name field updates (givenName, familyName).úname.givennamer|  rÏ   Núname.familynamerÒ   )r„  re   )rs  rz  rÍ   r×   s       rL   Ú_handle_name_updaterŠ  ‰  se   € àÐÒØ�hÒØ×Ñ˜k¨4Õ0ä),¨U«ˆM˜+Ò&Ø	Ð"Ò	"Ø�hÒØ×Ñ˜l¨DÕ1ä*-¨e«*ˆM˜,Ò'ð	 
#rh   Ú	teams_setc                 óÂ   — t        |«      }|s|€t        |d«      }| dk(  rt        |«      S | dk(  r|j                  |«       y| dk(  r|D ]  }|j	                  |«       Œ y)z(Handle group/team membership operations.NrÐ   ÚreplaceÚaddr|  )rr  ry  rž  r\   Údiscard)rz  rÍ   r‹  rs  Úgroup_valuesÚgids         rL   Ú_handle_group_operationsr’  —  sw   € ä(¨Ó/€LÙ˜E˜MÜ4°T¸8ÓDˆØ�)ÒÜ�<Ó Ð Ø	�EÒ	Ø×Ñ˜Ô&ð ð 
�HÒ	Øò 	#ˆCØ×Ñ˜cÕ"ð	#àrh   c                 óR   — | j                  dd«      d   j                  dd«      d   S )zVThe attribute name a SCIM path targets, stripped of any value filter or sub-attribute.ú[rK  r   ú.)Úsplit)rs  s    rL   Ú_multi_valued_attribute_baser—  ¦  s*   € à�:‰:�c˜1Ó˜aÑ ×&Ñ& s¨AÓ.¨qÑ1Ð1rh   c           	      ó  — t        | «      }t        |   }| |k7  rt        ddd|› d�i¬«      ‚|dk(  r|j                  |d«       y|€t        ddd|› d	|› d
�i¬«      ‚t	        |t
        «      r|n|g}	 t        j                  |«      }|D �cg c]  }|j                  d¬«      ‘Œ }	}|j                  |«      }
|dk(  rt	        |
t
        «      r	|
|	z   ||<   y|	||<   y# t        $ r t        ddd|› d�i¬«      ‚w xY wc c}w )zQHandle add/replace/remove for the entitlements and roles multi-valued attributes.r  rº   z6Filtered or sub-attribute paths are not supported for z; PATCH the full attributer»   r|  NzThe z operation on z3 requires a 'value' member (RFC 7644 Section 3.5.2)zInvalid value for z9: expected a list of objects with a 'value' sub-attributeTrÛ   rŽ  )r—  Ú)SCIM_MULTI_VALUED_ATTRIBUTE_METADATA_KEYSr   r„  r§  r“   ÚSCIM_MULTI_VALUED_LIST_ADAPTERr+  r   rÝ   rê   )rs  rz  rÍ   r�   ÚbaseÚmetadata_keyr&  ÚattrsÚattrÚdumpedrq  s              rL   Ú%_handle_multi_valued_attribute_updater   «  sI  € ä.¨tÓ4€DÜCÀDÑI€LØˆt‚|ÜØØÐUÐVZÐU[Ð[uÐvÐwô
ð 	
ð
 �(ÒØ�‰�\ 4Ô(Øà€}ÜØØ˜t G 9¨N¸4¸&Ð@sÐtÐuô
ð 	
ô
 ",¨E´4Ô!8™¸u¸g€Jð
Ü5×EÑEÀjÓQˆð EJÖJ¸D�T—_‘_°$�_Õ7ÐJ€FÐJØ—l‘l <Ó0€HØ�%ÒœJ x´Ô6Ø!)¨FÑ!2ˆ�ÑØØ#€Hˆ\Òøô ò 
ÜØØÐ1°$°Ð7pÐqÐrô
ð 	
ð
üò Ks   Á3C ÂC<ÃC9c                 ó>   — |dk(  r|j                  | d«       y||| <   y)z5Handle generic metadata operations for unknown paths.r|  N)r„  )rs  rz  rÍ   r�   s       rL   Ú_handle_generic_metadatar¢  Ð  s!   € à�(ÒØ�‰�T˜4Õ àˆ�Šrh   r™   Ú	patch_opsc           	      ó\  — i }| j                   xs i }|j                  di «      }t        | j                  xs g «      }d}|j                  D �]Ê  }|j
                  xs dj                  «       }|j                  }	|j                  }
|sßt        |	t        «      rÏ|	j                  «       D ]º  \  }}|j                  «       }|dk(  rt        |
||«       Œ)|dk(  rt        |
||«       Œ<|dk(  rt        |
||«       ŒO|t        v rt!        ||
||«       Œf|dk(  sŒlt        |t        «      sŒ}|j                  «       D ]+  \  }}|j                  «       }|dv sŒt#        d	|› �|
||«       Œ- Œ¼ �Œ|dk(  rt        |
|	|«       �Œ/|dk(  rt        |
|	|«       �ŒC|dk(  rt        |
|	|«       �ŒW|d
v rt#        ||
|	|«       �Œkt%        |«      t        v rt!        ||
|	|«       �ŒŒ|j'                  d«      r t)        |
|	||j
                  «      }|€�Œ¹|}�Œ½t+        ||
|	|«       �ŒÍ |�|n|}||d<   ||d<   ||fS )zAApply patch operations and return update data and final team set.r×   Nr#  r¥   ÚdisplaynameÚ
externalidrÎ   )Ú	givennameÚ
familynamezname.)rˆ  r‰  rÐ   r�   )r�   rê   rž  r�   Ú
Operationsrs  r%  rÍ   Úopr§  r”   r±  r†  r  r�  r™  r   rŠ  r—  Ú
startswithr’  r¢  )r™   r£  rS  r�   r×   r‹  Úreplace_team_setrª  rs  rÍ   rz  ÚkeyÚvalÚ	key_lowerÚname_keyÚname_valÚname_key_lowerÚnew_replace_setÚfinal_team_sets                      rL   Ú_apply_patch_opsrµ  Ø  sH  € ð
 -/€KØ#×,Ñ,Ò2°€HØ#Ÿ<™<¨¸Ó<€Mä!$ ]×%8Ñ%8Ò%>¸BÓ!?€IØ(,Ðà×"Ñ"ó ,EˆØ—‘’˜2×$Ñ$Ó&ˆØ—‘ˆØ—%‘%ˆñ œ
 5¬$Ô/Ø!ŸK™K›Mò ‘��SØŸI™I›K�	Ø Ò(Ü)¨'°3¸ÕAØ -Ò/Ü.¨w¸¸[ÕIØ ,Ò.Ü-¨g°s¸KÕHØÔ"KÑKÜ9¸)ÀWÈcÐS[Õ\Ø &Ó(¬Z¸¼TÕ-BØ.1¯i©i«kò Ñ*˜ (Ø)1¯©Ó)9˜Ø)Ð-HÒHÜ/Ø"'¨Ð'7Ð 8Ø 'Ø (Ø -õ	ñðñ( à�=Ò Ü& w°°{ÖCØ�\Ò!Ü% g¨u°kÖBØ�XÒÜ! '¨5°(Ö;ØÐ:Ñ:Ü  g¨u°mÖDÜ)¨$Ó/Ô3\Ñ\Ü1°$¸ÀÈÖQØ�_‰_˜XÔ&Ü6°wÀÀyÐRT×RYÑRYÓZˆOØÒ*Ø#2Ò ä$ T¨7°E¸8ÖDðY,Eð\ 1AÐ0LÑ,ÐR[€NØ -€Hˆ_ÑØ&€K�
ÑØ˜Ð&Ð&rh   Úexcc                 óf   — | j                   }t        |t        «      xr |j                  d«      dk(  S )z…True when team_member_delete reports the user was already absent from the
    team, which is the idempotent no-op case for a removal.rº   zUser not found in team)r½   r§  r”   rê   )r¶  r½   s     rL   Ú_is_user_not_in_team_errorr¸    s.   € ð —J‘J€FÜ�fœdÓ#ÒW¨¯
©
°7Ó(;Ð?WÑ(WÐWrh   T)ÚfrozenÚslotsc                   ó"   — e Zd ZU eed<   eed<   y)ÚRosterWriteFailurerÜ  r¼   N)rb   rc   rd   re   r©   rg   rI   rh   rL   r¼  r¼    s   … àÓØÔrh   r¼  c                 ó¾   — t        | t        «      r| j                  S t        | t        «      r1| j                  j                  «       rt        | j                  «      S dS y)Nr¹   )r§  r   r¼   r+   ÚcodeÚisdigitrg   )r¶  s    rL   Ú_roster_write_statusrÀ  %  sE   € Ü�#”}Ô%Ø�‰ÐÜ�#”~Ô&Ø #§¡× 0Ñ 0Ô 2Œs�3—8‘8‹}Ð;¸Ð;Ørh   c                   ó:   ‡ — e Zd ZdZdeedf   deddfˆ fd„Zˆ xZS )ÚSCIMRosterSyncErroraß  Every roster write in the batch was attempted; these are the ones that did not land.

    Rolling the successful ones back is not safe, since the compensating write can fail
    too and can strip a membership that pre-dated the push. Naming the exact failures
    instead lets the IdP's next push, which is idempotent, close the gap. handle_exception_on_proxy
    reads ``status_code`` off this, so a unanimous failure keeps its own status and a mixed
    batch reports 500.
    Úfailures.Ú	attemptedrF   Nc                 óø   •— t        d„ |D «       «      }|| _        t        |«      dk(  rt        t	        |«      «      nd| _        t        ‰| �  dt        |«      › d|› ddj                  d„ |D «       «      › �«       y )	Nc              3   ó4   K  — | ]  }|j                   –— Œ y ­wrH   r_  ©r?  Úfailures     rL   rA  z/SCIMRosterSyncError.__init__.<locals>.<genexpr>8  s   è ø€ Ò#P¸G G×$7Õ$7Ñ#PùrB  rK  r¹   zSCIM roster sync failed on z of zn team membership writes, leaving the roster partially updated. Retry the push to reconcile it. Failed writes: z; c              3   ó4   K  — | ]  }|j                   –— Œ y ­wrH   )rÜ  rÇ  s     rL   rA  z/SCIMRosterSyncError.__init__.<locals>.<genexpr>>  s   è ø€ Ò'TÀ¨×(;Õ(;Ñ'TùrB  )	r�  rÃ  rÌ   ÚnextÚiterr¼   ÚsuperÚ__init__Újoin)rK   rÃ  rÄ  ÚstatusesÚ	__class__s       €rL   rÍ  zSCIMRosterSyncError.__init__7  sy   ø€ Ü#Ñ#PÀxÔ#PÓPˆØ?GˆŒÜ?BÀ8»}ÐPQÒ?Q¤t¬D°«NÔ';ÐWZˆÔÜ‰ÑØ)¬#¨h«-¨¸¸Y¸Kð Hà"Ÿi™iÑ'TÈ8Ô'TÓTÐUðWõ	
rh   )	rb   rc   rd   r�   r  r¼  rg   rÍ  Ú__classcell__)rÐ  s   @rL   rÂ  rÂ  -  s3   ø„ ñð
 Ð'9¸3Ð'>Ñ!?ð 
ÈCð 
ÐTX÷ 
ñ 
rh   rÂ  ÚlabelÚwritec              ƒ   óü   K  — 	  |«       ƒ d{  –—†  y7 Œ# t         $ r}|j                  cY d}~S d}~wt        $ r=}t        j                  d| |«       t        | › d|› �t        |«      ¬«      fcY d}~S d}~ww xY w­w)zJRun one roster write and return what failed, so the caller can keep going.Nz!SCIM roster write failed (%s): %sz: )rÜ  r¼   rI   )rÂ  rÃ  rë   r   rÇ  r¼  rÀ  )rÒ  rÓ  rá   s      rL   Ú_attempt_roster_writerÕ  B  sx   è ø€ ðgÙ‹g�ˆð ð 	ùÜò Ø�z‰zÕûÜò gÜ×&Ñ&Ð'JÈEÐSTÔUÜ"°%°¸¸1¸#¨ÔL`ÐabÓLcÔdÐfÕfûðgüsO   ‚A<„
 Ž� “A<” –	A9Ÿ0ªA9«A<°A9¼2A4Á.A9Á/A<Á4A9Á9A<Úwritesc           
   ƒ   ó¶   K  — t        | D ��cg c]  \  }}t        ||«      ƒ d {  –—† ‘Œ c}}«      }t        t        j                  |«      «      S 7 Œ,c c}}w ­wrH   )r  rÕ  r   r_  )rÖ  rÒ  rÓ  Ú	per_writes       rL   Ú_collect_roster_write_failuresrÙ  N  sP   è ø€ ô Ð\b×cÉLÈEÐSXÔ$9¸%ÀÓ$G×GÑGÓcÓd€IÜ”×$Ñ$ YÓ/Ó0Ð0ð HúÓcùs%   ‚A�A
¤A¥A
«&AÁA
ÁAc           	   ƒ   ó.  K  — 	 t        t        |t        | d¬«      ¬«      t        t        j
                  ¬«      ¬«      ƒ d {  –—†  y 7 Œ# t        $ r?}|j                  t        j                  k7  r‚ t        j                  d| |«       Y d }~y d }~ww xY w­w)NrÂ   ©r‹   Úrole)rÅ   r  r\  r]  z+User %s is already in team %s, skipping add)r6   r,   r&   r.   r%   rû   r+   r$  r*   Úteam_member_already_in_teamr   r  ©r‹   rÅ   rá   s      rL   Ú_add_user_to_teamrß  U  s„   è ø€ ðdÜÜ%ØÜ g°FÔ;ôô -Ô7G×7SÑ7SÔTô
÷ 	
ò 	
ùô ò dØ�6‰6”_×@Ñ@Ò@ØÜ×"Ñ"Ð#PÐRYÐ[b×cÑcûðdüs@   ‚B„>A
 ÁAÁA
 ÁBÁA
 Á
	BÁ5BÂBÂBÂBc              ƒ   óô   K  — 	 t        t        || ¬«      t        t        j                  ¬«      ¬«      ƒ d {  –—†  y 7 Œ# t
        $ r-}t        |«      s‚ t        j                  d| |«       Y d }~y d }~ww xY w­w)Nr[  r\  r]  z*User %s is not in team %s, skipping remove)	r7   r-   r.   r%   rû   r   r¸  r   r  rÞ  s      rL   Ú_remove_user_from_teamrá  d  sm   è ø€ ðcÜ Ü(°À'ÔJÜ,Ô7G×7SÑ7SÔTô
÷ 	
ò 	
ùô ò cÜ)¨!Ô,ØÜ×"Ñ"Ð#OÐQXÐZa×bÑbûðcüs7   ‚A8„3? ·=¸? ¼A8½? ¿	A5Á#A0Á+A8Á0A5Á5A8r›  rœ  r�  c              ƒ   óº   ‡ K  — t        t        ˆ fd„|D «       ˆ fd„|D «       «      «      }t        |«      ƒ d{  –—† }|r|rt        |t	        |«      ¬«      ‚y7 Œ­w)a  
    Add or remove user from teams

    Handles duplicate membership gracefully (idempotent operation).
    A user already being in a team (on add) or already absent from it (on
    remove) is treated as a no-op, not an error.

    Every team is attempted before anything is reported, so one failing team cannot
    strand the others unattempted. When ``raise_on_error`` is True the writes that did
    not land are reported together, instead of a teams array the roster never received
    being persisted as a success.
    c              3   óP   •K  — | ]  }d ‰› d|› �t        t        ‰|«      f–— Œ y­w)úadd ú to N)r
   rß  ©r?  rÅ   r‹   s     €rL   rA  z(patch_team_membership.<locals>.<genexpr>„  s6   øè ø€ ò àð ˜�y  W IÐ.´Ô8IÈ7ÐT[Ó0\Ô]ñùó   ƒ#&c              3   óP   •K  — | ]  }d ‰› d|› �t        t        ‰|«      f–— Œ y­w)úremove ú from N)r
   rá  ræ  s     €rL   rA  z(patch_team_membership.<locals>.<genexpr>ˆ  s7   øè ø€ ò àð ˜7˜) 6¨'¨Ð3´WÔ=SÐU\Ð^eÓ5fÔgñùrç  N©rÄ  T)r  r   rÙ  rÂ  rÌ   )r‹   r›  rœ  r�  rÖ  rÃ  s   `     rL   rŸ  rŸ  p  sc   øè ø€ ô$ Üóà7ôóà<ôó		
ó€Fô ;¸6ÓB×B€HÙ‘NÜ! (´c¸&³kÔBÐBàð	 Cús   ƒ6A¹Aº Ac              ƒ   óÆ  K  — t        j                  d| |j                  «       «       	 t        «       ƒ d{  –—† }t	        | «      ƒ d{  –—† }t        |«      }t        ||¬«      \  }}|j                  d«      }t        t        |t        «      r|nd«      }t        | |j                  xs g t        |«      ¬«      ƒ d{  –—†  t        |«      |d<   t        «       ƒ d{  –—† }	|	�3t        t!        |t        |«      «      ƒ d{  –—† |	t#        «       «      |d<   d|v r't        |d   t$        «      rdd	lm}
  |
|d   «      |d<   t+        t-        |«      «      j/                  d
| i|¬«      ƒ d{  –—† }|�||€dn|k7  rt1        | | ¬«      ƒ d{  –—†  t3        j4                  |«      ƒ d{  –—† }|S 7 �Œ}7 �Œm7 Œù7 ŒÛ7 Œ¹7 ŒP7 Œ17 Œ# t6        $ r}t9        |«      ‚d}~ww xY w­w)z4
    Patch a user according to SCIM v2 protocol
    z*SCIM PATCH USER request for user_id=%s: %sN)r™   r£  r�   rŠ   r�   r‘   r   r!   r‹   r’   Tr­  )r   r  rÝ   r¿   rÃ   rÁ  rµ  rê   r¿  r§  r   r—   r�   r“   r÷   rü   r  rô   r”   Ú*litellm.litellm_core_utils.safe_json_dumpsr"   r{   r?   r\   r¹  r4   r˜   rë   r:   )r‹   r£  r„   r™   rP  rS  r´  Úpatched_metadatarT  r†   r"   r›   rB  rá   s                 rL   Ú
patch_userrï  •  sô  è ø€ ô ×ÑØ4ØØ×ÑÓôð2+Ü%JÓ%L×LˆÜ%7¸Ó%@×@ˆä.¨}Ó=ˆä&6Ø'Øô'
Ñ#ˆ�^ð
 #.§/¡/°*Ó"=ÐÜ.Ä:ÐN^Ô`gÔChÑ/?ÐnrÓsˆ
ô .ØØ(×.Ñ.Ò4°"Ü˜>Ó*ô
÷ 	
ð 	
ô  $ NÓ3ˆ�GÑä#8Ó#:×:ˆØÐ"Ü'>Ü0°ÄÀ^Ó@TÓU×UØÜ'Ó)ó(ˆK˜Ñ$ð ˜Ñ$¬°KÀ
Ñ4KÌTÔ)RÝMá&0°¸ZÑ1HÓ&IˆK˜
Ñ#ä$*¬>¸-Ó+HÓ$I×$PÑ$PØ˜gÐ&Øð %Qó %
÷ 
ˆð
 Ð! j¸[Ð=P±TÐVaÒ&bÜ(°ÀjÀ.ÔQ×QÐQä!4×!XÑ!XÐYeÓ!f×fˆ	àÐð]  MùØ@ùð	
øð ;øð Vøð
øð Røàfùô ò +Ü'¨Ó*Ð*ûð+üs¸   ‚&G!©G ¶F3·G Á	F6Á
A5G Â?F9Ã G ÃF;Ã #G ÄF=ÄA*G Å.F?Å/ G ÆGÆG Æ,GÆ-G Æ2G!Æ3G Æ6G Æ9G Æ;G Æ=G Æ?G ÇG ÇG Ç	GÇGÇGÇG!c                   ó   — e Zd ZU dZeed<   y)Ú_TeamWhereConditionszLThe team columns SCIM GET /Groups can filter on, as Prisma where-conditions.r  N)rb   rc   rd   r�   re   r©   rI   rh   rL   rñ  rñ  Ý  s
   … ÙVà„Orh   rñ  )Útotalrá  c              ƒ   óø  K  — t        j                  d| ||«       	 t        «       ƒ d{  –—† }i }|r,d|v r(|j                  d«      d   j	                  d«      }||d<   t        t        |«      «      j                  || dz
  |dd	i¬
«      ƒ d{  –—† }t        t        |«      «      j                  |¬«      ƒ d{  –—† }g }|D ]Ó  }	t        t        |	«      ƒ d{  –—† «      ƒ d{  –—† }
t        j                  d|
«       t        |	d|	j                  «      }|	j                  r|	j                  j                  «       nd}|	j                  r|	j                  j                  «       nd}t!        dg|	j                  ||
d||dœ¬«      }|j#                  |«       ŒÕ t        j                  d|«       t%        || t'        |t)        |«      «      |¬«      S 7 �Œ§7 �ŒF7 �Œ7 �Œ7 Œ÷# t*        $ r}t-        |«      ‚d}~ww xY w­w)z<
    Get a list of groups according to SCIM v2 protocol
    z9SCIM GET GROUPS request: startIndex=%s count=%s filter=%sNzdisplayName eqzdisplayName eq rK  z"'r  r3  r4  r5  r‰   zSCIM GET GROUPS members: %srâ  rà  )rÚ  rp  ÚlastModified)r  rÛ  ró  rx  rß  zSCIM GET GROUPS response: %sr7  )r   r  r¿   r–  r   r{   r>   rW   r`   r˜  rÍ  ÚgetattrrÅ   r3  Ú	isoformatÚ
updated_atÚ	SCIMGroupr•  r9  r:  rÌ   rë   r:   )r/  r`   r0  r„   r;  r  r�   r@  Úscim_groupsrÈ   rx  Úteam_created_atÚteam_updated_atÚ
scim_grouprá   s                  rL   Ú
get_groupsrý  ä  s  è ø€ ô ×ÑØCØØØô	ð8+Ü%JÓ%L×Lˆà8:ÐÙà 6Ñ)Ø#Ÿ\™\Ð*;Ó<¸QÑ?×EÑEÀeÓL�
Ø1;Ð  Ñ.ô $¤N°=Ó$AÓB×LÑLØ"Ø˜q‘.ØØ Ð(ð	 Mó 
÷ 
ˆô $*¬.¸Ó*GÓ#H×#NÑ#NÐUeÐ#NÓ#f×fˆð /1ˆØò 	+ˆDô
 6Ô<_Ð`dÓ<e×6eÓf×fˆGÜ ×&Ñ&Ð'DÀgÔNÜ   |°T·\±\ÓBˆJØ=A¿_º_˜dŸo™o×7Ñ7Ô9ÐRVˆOØ=A¿_º_˜dŸo™o×7Ñ7Ô9ÐRVˆOä"ØFÐGØ—<‘<Ø&Øà$+Ø.Ø$3ñô
ˆJð ×Ñ˜zÕ*ð-	+ô0 	×"Ñ"Ð#AÀ;ÔOÜØ$Ø!Ü˜U¤C¨Ó$4Ó5Ø!ô	
ð 	
ð_  Mùð
ùð gùð 7fùÐfùô6 ò +Ü'¨Ó*Ð*ûð+üs‚   ‚G:œG ©GªA#G ÂGÂ+G Â9GÂ:G ÃG
ÃG Ã$GÃ%C*G ÇG:ÇG ÇG ÇG ÇG ÇG Ç	G7Ç'G2Ç2G7Ç7G:z/Groups/{group_id}zGroup IDÚgroup_idc              ƒ   ó  K  — t        j                  d| «       	 t        | «      ƒ d{  –—† }t        j                  |«      ƒ d{  –—† }t        j                  d|«       |S 7 Œ97 Œ# t
        $ r}t        |«      ‚d}~ww xY w­w)z@
    Get a single group by ID according to SCIM v2 protocol
    z&SCIM GET GROUP request for group_id=%sNzSCIM GET GROUP response: %s)r   r  rÉ   r4   Ú$transform_litellm_team_to_scim_grouprë   r:   )rþ  rÈ   rü  rá   s       rL   Ú	get_groupr  3	  s{   è ø€ ô ×ÑÐGÈÔRð+Ü.¨xÓ8×8ˆä"5×"ZÑ"ZÐ[_Ó"`×`ˆ
Ü×"Ñ"Ð#@À*ÔMØÐð	 9øà`ùô ò +Ü'¨Ó*Ð*ûð+üsJ   ‚BšA& ¨A"©A& ÁA$ÁA& Á!BÁ"A& Á$A& Á&	A?Á/A:Á:A?Á?Br  rÊ  c                 ó  — t         j                  }t        |t        «      rt	        |«      n|�|j                  d¬«      ni }|j                  d«      }i t        |t        «      r|ni ¥t        di¥}t        j                  i |¥| |||dœ¥«      S )zˆBuild the SCIM group's team request, applying litellm.default_team_params
    (including models) the same way SSO auto-created teams do.TrÛ   r�   )rÅ   r  rÊ  r�   )
rð   Údefault_team_paramsr§  r”   r   rÝ   rê   r/  r'   r`  )rÅ   r  rÊ  Údefault_paramsÚdefaultsÚdefault_metadatar�   s          rL   Ú_new_team_request_with_defaultsr  K	  s²   € ô $×7Ñ7€Nô �n¤dÔ+ô 	�Ô ð Ð%ð ×&Ñ&°DÐ&Ô9àð ð 'Ÿl™l¨:Ó6ÐðÜ)Ð*:¼DÔAÑÀrðä&¨ñ€Hô ×(Ñ(ð	
Øð	
àØ$Ø"4Ø ò	
óð rh   c              ƒ   óT  K  — t        j                  d| j                  «       «       	 t        «       ƒ d{  –—† }| j                  xs+ | j
                  xs t        t        j                  «       «      }t        t        |«      «      j                  d|i¬«      ƒ d{  –—† }|rt        ddd|› �i¬«      ‚t        | «      ƒ d{  –—† }|j                  D �cg c]  }t        |d	¬
«      ‘Œ }}t!        t#        || j$                  |¬«      t'        dddœ¬«      t)        t*        j,                  ¬«      ¬«      ƒ d{  –—† }t/        ||j                  «      ƒ d{  –—†  t1        j2                  |«      ƒ d{  –—† }|S 7 �ŒE7 Œà7 Œ»c c}w 7 ŒM7 Œ17 Œ# t4        $ r}	t7        |	«      ‚d}	~	ww xY w­w)z6
    Create a group according to SCIM v2 protocol
    zSCIM CREATE GROUP request: %sNrÅ   r‰   rH  rº   zGroup already exists with ID: r»   rÂ   rÛ  )rÅ   r  rÊ  Úhttpz/scim/v2/Groups)r$  rs  )Úscoper\  )rY   Úhttp_requestr^  )r   r  rÝ   r¿   rÛ  rÑ   re   r    rJ  r{   r>   rQ   r   r�  r±   r&   r5   r  ró  r   r.   r%   rû   r  r4   r   rë   r:   )
rÔ   r„   rÅ   r  Úmember_resultrg  rÊ  Úcreated_teamrü  rá   s
             rL   Úcreate_groupr  j	  s’  è ø€ ô ×ÑØ'Ø×ÑÓôð#+Ü%JÓ%L×Lˆð Ÿ™ÒJ U×%5Ñ%5ÒJ¼¼T¿Z¹Z»\Ó9Jˆô &,¬N¸=Ó,IÓ%J×%VÑ%VÐ^gÐipÐ]qÐ%VÓ%r×rˆáÜØØÐ#AÀ'ÀÐ!KÐLôð ô &?¸uÓ%E×EˆØVc×VrÑVrÖsÈœf¨Y¸VÖDÐsÐÐsô %-Ü0ØØ ×,Ñ,Ø#5ôô
 !°Ð@QÑ'RÔSÜ,Ô7G×7SÑ7SÔTô%
÷ 
ˆô +¨=¸-×:VÑ:VÓW×WÐWä"5×"ZÑ"ZÐ[gÓ"h×hˆ
ØÐðA  Mùð  søð  FúÚsð
øð 	Xøàhùäò +Ü'¨Ó*Ð*ûð+üs    ‚%F(¨F µE:¶A&F ÂE=Â&F ÃE?ÃF ÃFÃ*AF Ä8FÄ9F ÅFÅF Å3F
Å4F Å9F(Å:F Å=F Å?F ÆF ÆF Æ
F Æ	F%ÆF Æ F%Æ%F(c              ƒ   óJ  K  — t        j                  d| |j                  «       «       	 t        «       ƒ d{  –—† }t	        | «      ƒ d{  –—† }t        |«      ƒ d{  –—† }t        j                  d|j                  «       t        j                  dt        |j                  «      «       |j                  r|j                  ni }i |¥t        |j                  «       t        di¥}|j                  t        |«      dœ}t        t        |«      «      j!                  d| i|¬«      ƒ d{  –—† }t#        t%        |«      ƒ d{  –—† «      }	t        j                  d	|	«       t#        |j                  «      }
t        j                  d
|
«       t'        | |	|
¬«      ƒ d{  –—†  |j(                  |j                  k7  }t+        ||r|	|
z  n|	|
z  «      ƒ d{  –—†  t-        j.                  |«      ƒ d{  –—† }|S 7 �Œ¾7 �Œ®7 �Œž7 ŒÖ7 ŒÀ7 Œg7 Œ27 Œ# t0        $ r}t3        |«      ‚d}~ww xY w­w)z6
    Update a group according to SCIM v2 protocol
    z*SCIM PUT GROUP request for group_id=%s: %sNz!SCIM PUT GROUP all_member_ids: %sz SCIM PUT GROUP created_users: %sT)r  r�   rÅ   r’   z"SCIM PUT GROUP current_members: %sz SCIM PUT GROUP final_members: %s)rþ  rc  Úfinal_members)r   r  rÝ   r¿   rÉ   r�  r±   rÌ   r°   r�   r0  r/  ró  r"   r{   r>   r\   rž  rÍ  Ú _handle_group_membership_changesr  r  r4   r   rë   r:   )rþ  rÔ   r„   r  r  Úexisting_metadataÚupdated_metadatarS  Úupdated_teamrc  r  Úalias_changedrü  rá   s                 rL   Úupdate_groupr   	  s!  è ø€ ô ×ÑØ4ØØ×ÑÓôð
6+Ü%JÓ%L×LˆÜ%7¸Ó%A×Aˆô &?¸uÓ%E×EˆÜ×"Ñ"Ð#FÈ×HdÑHdÔeÜ×"Ñ"Ð#EÄsÈ=×KfÑKfÓGgÔhð >K×=SÒ=S =×#9Ò#9ÐY[Ðð#
Øð#
ä'¨×)9Ñ)9Ó);Ü*¨Dñ#
Ðð  ×+Ñ+Ü"Ð#3Ó4ñ
ˆô %+¬>¸-Ó+HÓ$I×$PÑ$PØ˜hÐ'Øð %Qó %
÷ 
ˆô "%Ô+NÈ}Ó+]×%]Ó!^ˆÜ×"Ñ"Ð#GÈÔYÜ" =×#?Ñ#?Ó@ˆÜ×"Ñ"Ð#EÀ}ÔUä.ØØ+Ø'ô
÷ 	
ð 	
ð  -×7Ñ7¸5×;LÑ;LÑLˆÜ*ØÙ0=ˆ_˜}Ò,À?ÐUbÑCbó
÷ 	
ð 	
ô #6×"ZÑ"ZÐ[gÓ"h×hˆ
ØÐðe  MùØAùð  Fùð$
øð &^øð
	
øð	
øð iùô ò +Ü'¨Ó*Ð*ûð+üs¸   ‚&H#©H ¶G4·H Á	G7Á
H ÁG:ÁC	H Ä&G=Ä'H Ä>G?Ä?AH ÆHÆ6H ÇHÇH Ç-HÇ.H Ç3H#Ç4H Ç7H Ç:H Ç=H Ç?H ÈH ÈH ÈH È	H ÈHÈH È H#c              ƒ   óÊ  K  — t        j                  d| «       	 t        «       ƒ d{  –—† }t        | «      ƒ d{  –—† }t	        |«      ƒ d{  –—† }|D ]Ž  }t        t        |«      «      j                  d|i¬«      ƒ d{  –—† }|sŒ4|j                  xs g }| |v sŒI|D �cg c]
  }|| k7  sŒ	|‘Œ }}t        t        |«      «      j                  d|id|i¬«      ƒ d{  –—†  Œ� t        ||«      ƒ d{  –—†  t        t        |«      «      j                  d| i¬«      ƒ d{  –—†  t        d¬	«      S 7 �Œ7 Œû7 Œê7 Œ¹c c}w 7 Œb7 ŒN7 Œ"# t        $ r}	t        |	«      ‚d}	~	ww xY w­w)
z6
    Delete a group according to SCIM v2 protocol
    z)SCIM DELETE GROUP request for group_id=%sNr‹   r‰   r�   r’   rÅ   rV  r_  )r   r  r¿   rÉ   rÍ  r{   r?   rQ   r�   r\   r  r>   r^   r   rë   r:   )
rþ  r„   r  r‘  rg  rÂ   Úcurrent_teamsÚtr�   rá   s
             rL   Údelete_groupr  ë	  ss  è ø€ ô ×ÑÐJÈHÔUð+Ü%JÓ%L×LˆÜ%7¸Ó%A×Aˆä"EÀmÓ"T×Tˆ
ð $ò 	ˆIÜ¤¨}Ó =Ó>×JÑJÐR[Ð]fÐQgÐJÓh×hˆDÚØ $§
¡
Ò 0¨b�Ø˜}Ò,Ø,9Ö K q¸QÀ(»]¢Ð K�IÐ KÜ ¤°Ó!>Ó?×FÑFØ(¨)Ð4¸GÀYÐ;Oð Gó ÷ ñ ð	ô +¨=¸*ÓE×EÐEô ”^ MÓ2Ó3×:Ñ:À)ÈXÐAVÐ:ÓW×WÐWä CÔ(Ð(ð+  MùØAøàTøð iúò !Lðøð 	Føð 	Xùô ò +Ü'¨Ó*Ð*ûð+üsÀ   ‚E#šE §D3¨E ºD6»E ÁD8Á2E Â D:ÂE ÂE ÂE Â!
D<Â,D<Â0.E ÃEÃE Ã4EÃ5-E Ä"EÄ#E Ä2E#Ä3E Ä6E Ä8E Ä:E Ä<E ÅE ÅE Å	E ÅEÅE Å E#r  c              ƒ   óò  K  — i }i |j                   xs i ¥t        di¥}t        t        |«      ƒ d{  –—† «      }|j	                  «       }| j
                  D �]`  }|j                  xs dj                  «       }|j                  }	|j                  }
|dk(  r|
dk(  rd|d<   ŒJt        |	«      |d<   ŒY|dk(  r'|
dk(  r|j                  dd«       Œvt        |	«      |d<   Œ…|j                  d	«      r­|	�t        |	«      n%t        d
„ t        |j                  d	«      D «       «      }|
dk(  r"|t!        |t#        |«      |«      ƒ d{  –—† z
  }Œðt%        |d|¬«      ƒ d{  –—† }|
dk(  rt        |j&                  «      }�Œ"|
dk(  s�Œ)|t        |j&                  «      z  }�ŒC|
dk(  r|j                  |d«       �Œ\|	||<   �Œc ||d<   t)        d„ | j
                  D «       «      }|rt        |«      nd}|||fS 7 �Œ¾7 Œ³7 Œœ­w)aD  Process patch operations for a group and return update data, final members
    and, when the request contained a member ``replace`` op, the absolute target
    roster it declared (``None`` otherwise).

    ``add``/``remove`` are deltas relative to the current roster, but ``replace``
    is absolute: it declares the roster is exactly this set, so the caller must
    reconcile against it as a set-to-target rather than rebasing it onto a
    concurrently-mutated roster.

    A ``remove`` drops the ids it names without resolving them first. Removal is
    idempotent and cannot put anything on a roster, while resolving would make it
    conditional on what the id turns out to be and leave members we should never
    have admitted - the phantom users this endpoint used to create for nested
    groups - impossible to clean up.
    TNr#  r¥  r|  r  r¦  rÑ   rx  c              3   ó4   K  — | ]  }t        |¬ «      –— Œ y­w)rJ  N)r–  )r?  rg  s     rL   rA  z2_process_group_patch_operations.<locals>.<genexpr>G
  s   è ø€ ò Ø4=”J Y×/Ð/ñùrB  Úscim_group_patchr�  r�  rŽ  r�   c              3   ó˜   K  — | ]B  }|j                   d k(  xr- |j                  xs dj                  «       j                  d«      –— ŒD y­w)r�  r#  rx  N)rª  rs  r%  r«  )r?  rª  s     rL   rA  z2_process_group_patch_operations.<locals>.<genexpr>c
  sB   è ø€ ò (ØQSˆ�‰�ÑÒL §¡¢¨2×4Ñ4Ó6×AÑAÀ)ÓLÓLñ(ùs   ‚AA
)r�   r/  rž  rÍ  Úcopyr©  rs  r%  rÍ   rª  re   r„  r«  rp  r  ry  r†  r�  rŒ  r±   ra  )r£  r  r„   rS  r�   rc  r  rª  rs  rÍ   rz  Úpatched_membersr  Úmember_replace_presentÚreplace_targets                  rL   Ú_process_group_patch_operationsr#  
  s(  è ø€ ð$ -/€Kð _˜-×0Ñ0Ò6°BÐ^Ô9WÐY]Ñ^€Hô !Ô'JÈ=Ó'Y×!YÓZ€OØ#×(Ñ(Ó*€Mð ×"Ñ"ó ,'ˆØ—‘’˜2×$Ñ$Ó&ˆØ—‘ˆØ—%‘%ˆà�=Ò Ø˜(Ò"Ø,0�˜LÒ)ä,/°«J�˜LÒ)Ø�\Ò!Ø˜(Ò"Ø—‘˜\¨4Õ0ä),¨U«�˜Ò&Ø�_‰_˜YÔ'ð Ð$ô & eÔ,äñ ÜA^Ð_a×_fÑ_fÐhqÓArôó ð ð ˜(Ò"Ø -Ô6IØ#¤Y¨}Ó%=¸}ó7÷ 1ñ !‘ô '@Ø+Ø 2Ø"/ô'÷ !�ð
 ˜iÒ'Ü$'¨×(DÑ(DÓ$E’MØ Ô%Ø$1´C¸×8TÑ8TÓ4UÑ$U’Mð ˜(Ò"Ø—‘˜T 4Ö(à!&�˜“ðY,'ð\ '€K�
Ñä$'ñ (ØW`×WkÑWkô(ó %Ðñ 3IœC Ô.Èd€Nà˜ ~Ð5Ð5ðs "Zùð<1øð!ús;   ‚0G7²G0³DG7Ä?G3Å G7ÅG5Å%G7Æ A1G7Ç3G7Ç5G7c              ƒ   ó$  K  — d|v r$t        |d   t        «      rt        |d   «      |d<   |r0t        |«      j                  j                  d| i|¬«      ƒ d{  –—† S t        |«      j                  j                  d| i¬«      ƒ d{  –—† S 7 Œ37 Œ­w)a·  Apply the group's metadata/displayName patch updates to the database.

    Membership itself is not written here; it is reconciled onto the source of
    truth (members_with_roles and each member's user.teams) by
    _handle_group_membership_changes via team_member_add/team_member_delete.
    Writing the legacy `members` column here too would create a second, unread
    copy of membership that could drift from the source of truth.
    r�   rÅ   r’   Nr‰   )r§  r”   r"   r>   r�   r\   rQ   )rþ  rS  r„   s      rL   Ú_apply_group_patch_updatesr%  k
  sŸ   è ø€ ð �[Ñ ¤Z°¸JÑ0GÌÔ%NÜ",¨[¸Ñ-DÓ"Eˆ�JÑáÜ# MÓ2×8Ñ8×?Ñ?Ø˜hÐ'Øð @ó 
÷ 
ð 	
ô   Ó.×4Ñ4×@Ñ@È	ÐS[ÐG\Ð@Ó]×]Ð]ð	
øð ^ús$   ‚ABÁBÁ.BÂBÂBÂBrc  r  c              ƒ   óF  ‡ K  — t        ||z
  «      }t        ||z
  «      }t        j                  d|«       t        j                  d|«       t        t	        ˆ fd„|D «       ˆ fd„|D «       «      «      }t        |«      ƒ d{  –—† }|rt        |t        |«      ¬«      ‚y7 Œ­w)a)  Reconcile the group roster, attempting every member before reporting failures.

    Aborting on the first failure would leave the remaining members unattempted on top
    of unrolled-back, so every member is written and the ones that failed are named for
    the IdP's next push to reconcile.
    zmembers_to_add: %szmembers_to_remove: %sc           	   3   óX   •K  — | ]!  }d |› d‰› �t        t        |‰gg d¬«      f–— Œ# y­w)rä  rå  Trš  N©r
   rŸ  ©r?  rg  rþ  s     €rL   rA  z3_handle_group_membership_changes.<locals>.<genexpr>Ž
  sF   øè ø€ ò ð ð ˜9˜+ T¨(¨Ð4ÜÜ-Ø )Ø2:°Ø68Ø'+ôô	ñùó   ƒ'*c           	   3   óX   •K  — | ]!  }d |› d‰› �t        t        |g ‰gd¬«      f–— Œ# y­w)ré  rê  Trš  Nr(  r)  s     €rL   rA  z3_handle_group_membership_changes.<locals>.<genexpr>›
  sF   øè ø€ ò ð ð ˜i˜[¨¨x¨jÐ9ÜÜ-Ø )Ø13Ø7?°jØ'+ôô	ñùr*  Nrë  )r‚  r   r  r  r   rÙ  rÂ  rÌ   )rþ  rc  r  Úmembers_to_addÚmembers_to_removerÖ  rÃ  s   `      rL   r  r  
  s©   øè ø€ ô # =°?Ñ#BÓC€NÜ% o¸Ñ&EÓFÐä×ÑÐ3°^ÔDÜ×ÑÐ6Ð8IÔJäÜóð "0ôóð "3ôó	
ó€Fô< ;¸6ÓB×B€HÙÜ! (´c¸&³kÔBÐBð ð Cús   ƒA>B!ÂBÂB!c              ƒ   óx  K  — t        j                  d| |j                  «       «       	 t        «       ƒ d{  –—† }t	        | «      ƒ d{  –—† }t        |||«      ƒ d{  –—† \  }}}t        t        |«      ƒ d{  –—† «      }||z
  }||z
  }	t        | ||«      ƒ d{  –—† }
t        t        |«      «      j                  d| i¬«      ƒ d{  –—† }|r=t        t        t        j                  |j                  «       «      «      ƒ d{  –—† «      n|}|�|n||z  |	z
  }t        | ||«      ƒ d{  –—†  |j                  d|j                   «      }||j                   k7  }t#        ||r||z  n||z  «      ƒ d{  –—†  t        t        |«      «      j                  d| i¬«      ƒ d{  –—† }|r|}
|
€t%        ddd| › �i¬	«      ‚t'        j(                  t        j                  |
j                  «       «      «      ƒ d{  –—† }|S 7 �ŒË7 �Œ»7 �Œ©7 �Œ�7 �Œp7 �ŒE7 �Œ7 Œè7 Œ¡7 Œu7 Œ!# t*        $ r}t-        |«      ‚d}~ww xY w­w)
z5
    Patch a group according to SCIM v2 protocol
    z,SCIM PATCH GROUP request for group_id=%s: %sNrÅ   r‰   r  rÁ   rº   rÇ   r»   )r   r  rÝ   r¿   rÉ   r#  rž  rÍ  r%  r{   r>   rQ   r#   r`  r  rê   r  r  r   r4   r   rë   r:   )rþ  r£  r„   r  rS  r  r"  Úsnapshot_membersÚintended_addÚintended_remover  Úrefreshed_teamÚrefreshed_currentÚeffective_finalÚ	new_aliasr  Ú
final_teamrü  rá   s                      rL   Úpatch_groupr7  ¯
  sv  è ø€ ô ×ÑØ6ØØ×ÑÓôð;+Ü%JÓ%L×LˆÜ%7¸Ó%A×Aˆô <[Ø�} mó<
÷ 6
Ñ2ˆ�] Nô #&Ô,OÐP]Ó,^×&^Ó"_ÐØ+Ð.>Ñ>ˆØ!1°MÑ!Aˆô 8¸À+È}Ó]×]ˆä&,¬^¸MÓ-JÓ&K×&WÑ&WÐ_hÐjrÐ^sÐ&WÓ&t× tˆñ
 ô Ü9Ô:K×:ZÑ:ZÐ[i×[tÑ[tÓ[vÓ:wÓx×xôð "ð 	ð -Ð8‰NÐ?PÐS_Ñ?_ÐcrÑ>rð 	ô /¨xÐ9JÈOÓ\×\Ð\ð
 'Ÿ?™?¨<¸×9QÑ9QÓRˆ	Ø(¨M×,DÑ,DÑDˆÜ*ØÙ4AÐ Ò0ÐGXÐ[jÑGjó
÷ 	
ð 	
ô #)¬¸Ó)FÓ"G×"SÑ"SÐ[dÐfnÐZoÐ"SÓ"p×pˆ
ÙØ%ˆLàÐÜØØÐ#<¸X¸JÐ!GÐHôð ô #6×"ZÑ"ZÜ×,Ñ,¨\×-DÑ-DÓ-FÓGó#
÷ 
ˆ
ð Ððo  MùØAùð6
ùð '_ùð
 ^ùà tùð yùð 	]øð	
øð qøð
ùô
 ò +Ü'¨Ó*Ð*ûð+üsî   ‚&H:©H ¶H·H Á	HÁ
H ÁHÁH Á:H
Á;"H ÂHÂ-H ÃHÃ:H ÄHÄ&H Ä-HÄ.AH Å6HÅ7-H Æ$HÆ%AH Ç:HÇ;H È H:ÈH ÈH ÈH È
H ÈH ÈH ÈH ÈH ÈH ÈH ÈH È	H7È'H2È2H7È7H:ra   )rü  )r²   )F)år�   r'  Úcollections.abcr   r   r   r   r   r  r   Údataclassesr	   Ú	functoolsr
   Ú	itertoolsr   Útypingr   r   r   r   r   Úfastapir   r   r   r   r   r   r   r   Úpydanticr   r   r   Útyping_extensionsr   r   r   rð   Úlitellm._loggingr   Úlitellm._uuidr    rí  r"   Úlitellm.proxy._typesr#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   Úlitellm.proxy.auth.auth_checksr/   Ú$litellm.proxy.auth.user_api_key_authr0   Ú-litellm.proxy.common_utils.http_parsing_utilsr1   rÆ  r3   Ú<litellm.proxy.management_endpoints.scim.scim_transformationsr4   Ú1litellm.proxy.management_endpoints.team_endpointsr5   r6   r7   Úlitellm.proxy.utilsr8   r9   r:   Ú'litellm.repositories.table_repositoriesr;   r<   r=   Ú$litellm.repositories.team_repositoryr>   Ú$litellm.repositories.user_repositoryr?   Ú2litellm.repositories.verification_token_repositoryr@   Ú0litellm.types.proxy.management_endpoints.scim_v2Úprisma.modelsrB   ÚPrismaVerificationTokenrD   rj   rq   ru   r{   rf   rƒ   r¡   r®   r¶   r©   r¿   re   rÃ   rÉ   rŸ   rÕ   rª   r«   r“   r¬   r”   rã   rî   r  rû   ÚPROXY_ADMIN_VIEW_ONLYÚINTERNAL_USERrò   ÚScimUserRolerô   r÷   r   rü   r  r  r  r  r  r  ÚUnionÚ_ClassifiedGroupMemberr  r–  r!  r'  r(  r,  r2  r4  rg   r  rF  rO  rS  ra  r�  rd  rh  rj  rl  rr  rw  r†  rŒ  rø  r�  r˜  r—   r¥  r¨  r¹  r½  r¿  rÁ  ro  rÍ  rÑ  rã  rä  rú  rü  rê   r  r  r  r  r  r   r!  r*  r9  rC  rE  ÚpostrM  ÚputrU  r^   rg  rl  rp  rr  ry  r  r�  r†  rŠ  rž  r’  r—  r   r¢  ÚSCIMPatchOprµ  r¸  r¼  rë   rÀ  rÂ  rÕ  rÙ  rß  rá  rŸ  Úpatchrï  rñ  rý  r  r  r  r  r  r#  r%  r  r7  rI   rh   rL   ú<module>rY     s™  ðòó 
ß LÕ LÝ Ý !Ý Ý ß GÕ G÷	÷ 	ó 	÷ =Ñ <ß ?Ñ ?ã Ý 1Ý Ý A÷÷ ÷ ó õ DÝ BÝ SÝ Oõ÷ñ ÷
ñ ÷
ñ õ
 @Ý ?õô ?áÝRôQ�xô Qô(Q�xô Qô$/ Hô /ôI (ô Ið 
Ø ?�~Ð ?Ð*:Ò ?ó 
Ø ?ð 
Ø ?�~Ð ?Ð*:Ò ?ó 
Ø ?ð 
Ø YÐ2Ð YÐ7TÒ Yó 
Ø Yð 
ð&Ø(Ð+KÑKÐNfÑfð&à ò&ó 
ð&ð
ØØñà!ñ"ð ñð 'ñ	'ð
 ñðð ó÷H[ñ H[ôV1�9ô 1ô	 )ô 	ñ ØØ
)Ð	*ÙÐ-Ó.Ð/ô€ˆUó òð	 cð 	Ð.?ó 	ð	 cð 	Ð.?ó 	ð (ð ¨|ó ð> Ø,0Ø:>Ø37òØ�d‘
ðà�t‘ðð �4‰Kðð # TÑ)ð	ð
 Ð/Ñ0°4Ñ7ðð Ð(Ñ)¨DÑ0ðð 
ˆ#ˆvˆ+Ñóð>¨Tó ð. Ø× Ñ Ø×*Ñ*Ø×"Ñ"Ø×,Ñ,ð.ñ€ð4 ó 4ð S¨4¡Zó ð$Ø�Ñðà�t‘ðð ðð ˜Ñó	ð(°Lð ÈDÐQTÉIð ÐZ^Ð_lÑZmó ð$
°lð 
ÈhÐWZÉmð 
Ð`dó 
ô6˜*ô ôF˜*ô Fô
�Zô ô�zô ð Ð2Ð4GÈÐYiÐiÑjÐ ô&˜*ô &ð˜*ð ¨ó ð Jð °3¸±:ó ñ *¨$¨s°F¨{Ñ*;Ó<Ð �eÓ <ð˜Vð ¨°°V°Ñ(<¸tÑ(Có ð
s°fð 
sÀó 
sô˜Iô ð DEò=Øð=Ø+ð=Ø69¸D±jð=à
ˆ3�ˆ8�_ó=ðBR'¨ð R'ÀLð R'ÐUkó R'ðj Ð2ð  Ð7Jó  ð¨hÐ7MÑ.Nð ÐSfó ð	 Ð5ð 	 ÀIÈcÁNð 	 ÐWZÐ]aÑWaó 	 ð XÐ.DÑ%Eð ÐT]Ð^aÑTbð ÐglÐmpÐruÐmuÑgvó ô�9ô ô�yô ð8Øð8àð8ð  ð8ð �tÑó	8ð4 Cð °¸3±ð È5ÐQTÐVYÐQYÉ?ð Ð_dÐehÐjmÐemÑ_nó ð&*MØ�jÑ!ð*MØ+4°S©>ð*MØJVð*Màˆs�^ó*MðZIØ�jÑ!ðIàðIð  ðIð !ó	IðX¨9ð Ð9Tó ð,°°S±	ð ¸dÀ:Ñ>Nó ð
Øð
à˜‘Ið
ð �C‰yð
ð 
ó	
ð2 $2Ð ˜5Ó 1ðZ Fð Z¨tó Zð
;¨#ð ;¸ð ;Àó ;ð|b°|ð bÐQTð bÐY]ó bð( ¨¨f¨Ñ!5¸Ñ!<ð ÀÈÁó ð(Ð-ð (°$¸±+ó (ò+¨cð +Àð +ÐWfÐimÑWmó +ð\Ð4Eð È$ÈsÉ)ó ð&?¨(ó ?ò #ð °xÐ@PÑ7Qó ð8M�h˜zÑ*ó Mð` ‡‚ØØÙÐ+Ó,©gÐ6KÓ.LÐMð ó ð
 ‡‚ØØØÙÐ+Ó,©gÐ6KÓ.LÐMð	 ó ð ò óóðð* ‡‚ØØÙÐ+Ó,©gÐ6KÓ.LÐMð ó ð
 gò óð
ð( ‡‚Ø'ØÙÐ+Ó,©gÐ6KÓ.LÐMð ó ñ ! Ð,=Ô>ñØðàòóð
ð& ‡‚ØØÙÐ+Ó,©gÐ6KÓ.LÐMð ó ð
˜wò óð
ð& ‡‚ØØÙÐ+Ó,©gÐ6KÓ.LÐMð ó ñ ˜# \Ô2ñØðàòóð
ð$ ‡‚ØØ,ØÙÐ+Ó,©gÐ6KÓ.LÐMð	 ó ð0¨wò 0óð0ð	2 sð 	2¨u°S¸#°X©ÀÑ/Eó 	2ð ‡‚ØØ#ØÙÐ+Ó,©gÐ6KÓ.LÐMð	 ó ñ ˜A !”nÙ�r˜a CÔ(Ù˜t›ñ:+Øð:+àð:+ð �$‰Jò:+óð:+ðz ‡‚ØØØÙÐ+Ó,©gÐ6KÓ.LÐMð	 ó ñ ˜ 9Ô-ñ+Øò+óð+ð$ ×ÒØØØÙÐ+Ó,©gÐ6KÓ.LÐMð	 ó ñ ˜#“YñA+Ø
òA+óðA+ðH ‡‚ØØØÙÐ+Ó,©gÐ6KÓ.LÐMð	 ó ñ ˜ 9Ô-Ù˜#“YñJ+ØðJ+à
òJ+óðJ+ðZ ×ÒØØÙÐ+Ó,Ð-ð ó ñ ˜ 9Ô-ñ-+Øò-+óð
-+ð`˜vð ¨*°tÑ*;ó ð,o ð o¨E°*¸c°/Ñ,Bó oðE ð E¨D°©Ió Eð
,¨¨d©
ð ,¸sð ,ÀtÈCÁyó ,ð2/¨ð /°Fð /ÈÈcÐSYÈkÑIZð /Ð_có /ð0 sð 0°6ð 0ÈÈSÐRXÈ[ÑHYð 0Ð^bó 0ð
+ 3ð 
+¨vð 
+ÀÀcÈ6ÀkÑARð 
+ÐW[ó 
+ð5˜cð 5¨Cð 5¸ð 5ÈtÐTWÐY_ÐT_ÑO`ð 5Ðeió 5ð cð °&ð ÀSÈÁXð ÐUXÐ[_ÑU_ð ÐdgÐhkÑdlÐosÑdsó ð2 sð 2¨só 2ð
"$°ð "$¸cð "$È&ð "$Ð\`ÐadÐflÐalÑ\mð "$Ðrvó "$ðJ 3ð °ð ¸Vð ÈtÐTWÐY_ÐT_ÑO`ð Ðeió ð='Ø$ð='àð='ð ˆ4��V�Ñ˜c #™hÐ&Ñ'ó='ð@X Mð X°dó Xñ �$˜dÔ#÷ð ó $ðð
˜ið ¨Có ô
˜)ô 
ð*	 sð 	°8¸BÀ	È&Ñ@QÐ<QÑ3Rð 	ÐW\Ð]oÐqtÐ]tÑWuó 	ð1Ø�U˜3 ¨¨Y°vÑ->Ð)>Ñ ?Ð?Ñ@ÑAð1à
Ð˜sÐ"Ñ#ó1ðd Sð d°3ð d¸4ó dð	c¨#ð 	c¸ð 	cÀó 	cð  !ò	"Øð"à" 3™ið"ð $(¨¡9ð"ð ð	"ð
 
ó"ðJ ×ÒØØØÙÐ+Ó,©gÐ6KÓ.LÐMð	 ó ñ ˜ 9Ô-Ù! #›Yñ?+Øð?+àò?+óð?+ôD˜9¨Eõ ð ‡‚ØØ#ØÙÐ+Ó,©gÐ6KÓ.LÐMð	 ó ñ ˜A !”nÙ�r˜a CÔ(Ù˜t›ñF+ØðF+àðF+ð �$‰JòF+óðF+ðR ‡‚ØØØÙÐ+Ó,©gÐ6KÓ.LÐMð	 ó ñ ˜ JÔ/ñ+Øò+óð+ð$Øðà�d‘
ðð ! Ñ(ðð ó	ð> ×ÒØØØÙÐ+Ó,©gÐ6KÓ.LÐMð	 ó ñ ˜C“yñ-+Øò-+óð-+ð` ‡‚ØØØÙÐ+Ó,©gÐ6KÓ.LÐMð	 ó ñ ˜ JÔ/Ù˜C“yñB+ØðB+àòB+óðB+ðJ ×ÒØØÙÐ+Ó,Ð-ð ó ñ ˜ JÔ/ñ +Øò +óð
 +ðFU6ØðU6Ø+<ðU6ØMYðU6à
ˆ4��V�Ñ˜c #™h¨¨C©°4©Ð7Ñ8óU6ðp^¨sð ^ÀÀcÈ6ÀkÑARð ^Ðcoó ^ð(-C°Sð -CÈ3ÈsÉ8ð -CÐdgÐhkÑdlð -CÐquô -Cð` ×ÒØØØÙÐ+Ó,©gÐ6KÓ.LÐMð	 ó ñ ˜ JÔ/Ù! #›YñH+ØðH+àóH+óñH+rh   