
    epja                        d Z ddl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mZ dddZdddZddZ G d d          ZdS )a  User-authorization methods for ``GatewayRunner``.

Extracted from ``gateway/run.py`` as part of the god-file decomposition campaign
(``~/.hermes/plans/god-file-decomposition.md``, Phase 3 mechanical mixin lifts).
This mixin holds the inbound-message authorization cluster: whether a user/chat
is allowed to talk to the agent, the per-adapter DM policy, and the
unauthorized-DM behavior.

Behavior-neutral: every method is lifted verbatim from ``GatewayRunner``.
``self.*`` calls resolve unchanged via the MRO. Neutral dependencies import at
module top; the module-level ``logger`` is imported lazily inside the one method
that uses it (``from gateway.run import logger`` resolves at call time, when
``gateway.run`` is fully loaded) so this module never imports ``gateway.run`` at
import time -> no import cycle. The lazy import preserves the exact logger name
(``"gateway.run"``) so log records are unchanged.
    )annotationsN)Optional)Platform)SessionSource)expand_whatsapp_aliasesnormalize_whatsapp_identifier namestrdefaultreturnc                (   | s|S 	 ddl m}  ||           }|Bt          |                                          r!t          |                                          S n# t          $ r Y nw xY wt          j        |           p|                                S )zERead allowlist/auth env; prefer profile secret_scope under multiplex.r   )
get_secret)agent.secret_scoper   r   strip	Exceptionosgetenv)r
   r   r   vals       9/home/thesage/.hermes/hermes-agent/gateway/authz_mixin.py	_auth_envr      s     111111j?s3xx~~//?s88>>###   IdOO&w--///s   AA 
A)(A)c                .   | s|S 	 ddl m}m}  |            }|D |            r:|                    |           }||S t	          |                                          S n# t          $ r Y nw xY wt          j        |           p|                                S )u  Read a platform allow/deny gate env var with per-profile isolation.

    Like ``_auth_env`` but authoritative under multiplex: when a profile
    secret scope is installed AND multiplexing is active, a key absent from
    the scope returns ``default`` instead of falling through to
    ``os.environ``. Under multiplex the process env may hold ANOTHER
    profile's first-writer-bridged value (the YAML→env bridges in the
    Discord/Telegram adapters' ``_apply_yaml_config`` are first-writer-wins),
    so falling through would leak profile A's allowlist into profile B
    (issue #72348). Single-profile deployments — no scope installed, or
    multiplex off — behave exactly like the legacy ``os.getenv`` read.
    r   )current_secret_scopeis_multiplex_active)	r   r   r   getr   r   r   r   r   )r
   r   r   r   scoper   s         r   _platform_gate_envr   .   s      
PPPPPPPP$$&&!4!4!6!6))D//C{s88>>###   IdOO&w--///s   6A  A 
A,+A,set[str]c                    | t                      S t          | t                    rd | D             S d t          |                               d          D             S )aG  Parse allowlist values from config or env var into a set of strings.

    Handles both list inputs (YAML sequences) and comma-separated string
    inputs (env vars or scalar YAML values).  A scalar string is split on
    commas so ``allow_from: "123,456"`` yields ``{"123", "456"}``, not
    ``{"1", "2", "3", ",", ...}``.
    Nc                    h | ]D}t          |                                          #t          |                                          ES  r   r   .0parts     r   	<setcomp>z$_coerce_allow_set.<locals>.<setcomp>V   s=    GGGdSYY__5F5FGD		!!GGG    c                ^    h | ]*}|                                 |                                 +S r!   r   r#   s     r   r&   z$_coerce_allow_set.<locals>.<setcomp>W   s-    IIITDJJLLIDJJLLIIIr'   ,)set
isinstancelistr   split)raws    r   _coerce_allow_setr0   K   s_     {uu#t HGGcGGGGIISXX^^C%8%8IIIIr'   c                      e Zd ZdZ	 dddZdd
ZddZd dZddd!dZddd!dZ	ddd"dZ
ddd"dZddd#dZd$dZd%dZddd"dZdS )&GatewayAuthorizationMixinz6User/chat authorization methods for ``GatewayRunner``.NplatformOptional[Platform]profileOptional[str]c                   |sdS |pd                                 pd}|r|dk    rd}t          | dd          }t          |          r	  |            }n# t          $ r d}Y nw xY w||k    r(t          | dd          pi }|                    |          S t          | dd          pi }||v r||                             |          S dS t          | dd          pi }|                    |          S )a  Resolve the live adapter whose intake policy should gate authorization.

        In multiplex mode, secondary-profile adapters live in
        ``_profile_adapters[profile]`` while the default/active profile uses
        ``self.adapters``. ``SessionSource.profile`` selects which map to consult.
        When a stamped profile has its own adapter registry entry, the default
        profile's same-platform adapter must not be consulted as a fallback.
        Nr	   r   _active_profile_nameadapters_profile_adapters)r   getattrcallabler   r   )selfr3   r5   profile_nameactive_profileactive_profile_fnr9   profile_adapterss           r   _authorization_adapterz0GatewayAuthorizationMixin._authorization_adapter]   s>     	42,,..6$ 	LI55!N '.Dd K K)** **%6%6%8%8NN  * * *%)NNN*~--"4T::@b||H---&t-@$GGM2///'599(CCC 44T228b||H%%%s   
A A"!A"sourceOptional[SessionSource]c                .   |dS |                      |          }||S t          |dd          du r2t          | dd          pi }|                    t          j                  S |                     t          |dd          t          |dd                    S )z:Resolve the live adapter for an inbound ``SessionSource``.Ndelivered_via_upstream_relayFTr9   r3   r5   )_registered_transport_adapterr;   r   r   RELAYrB   )r=   rC   transport_adapterr9   s       r   _adapter_for_sourcez-GatewayAuthorizationMixin._adapter_for_source   s    >4 >>vFF($$ 695AATII
 tZ66<"H<</// **FJ--FIt,,
 
 	
r'   r   c                j   t          |dd          }t          |          r
 |            nd}t          |dd          }||dS |t          | dd          pi                     |          u r|S t          | dd          pi }|                                D ]}||                    |          u r|c S dS )a  Return the registered adapter that created *source*, if retained.

        ``source.profile`` is the runtime/session namespace. A chat-based
        profile route can therefore differ from the adapter profile when one
        shared credential serves several routed runtimes. ``build_source``
        keeps the receiving adapter as in-process provenance so replies and
        intake-policy checks stay on that transport without weakening the
        fail-closed fallback for restored or hand-built sources.
        _transport_adapter_refNr3   r9   r:   )r;   r<   r   values)r=   rC   adapter_refadapterr3   profile_mapsrA   s          r   rG   z7GatewayAuthorizationMixin._registered_transport_adapter   s     f&>EE#+K#8#8B++---d6:t44?h.4wtZ66<"AA(KKKKNt%8$??E2 , 3 3 5 5 	 	*..x8888 9tr'   r   c                T   |                      |          }t          |dd          }|p|t          | dd          pi                     |          u rdS t          | dd          pi                                 D ] \  }}||                    |          u r|c S !t          |dd          S )z@Resolve the transport-owning profile for adapter policy lookups.r3   Nr9   r:   r5   )rG   r;   r   items)r=   rC   rO   r3   r5   rA   s         r   _adapter_profile_for_sourcez5GatewayAuthorizationMixin._adapter_profile_for_source   s    44V<<6:t4474T::@bEEhOOOOt1488>Begg# #)) .228<<<<"NNN =vy$///r'   r5   boolc               z    |sdS |                      ||          }|dS t          t          |dd                    S )a  Whether the adapter for *platform* delegates authz to a trusted upstream.

        Mirrors ``BasePlatformAdapter.authorization_is_upstream``. The relay
        adapter sets this True: the Team Gateway connector authenticates the
        gateway's WS and resolves owner-only author bindings before delivering,
        so an inbound relay event is already authorized as this instance's bound
        user. Unlike ``_adapter_enforces_own_access_policy`` (a LOCAL config
        policy the gateway mirrors only when it's an allowlist), this is an
        UPSTREAM decision the gateway honors directly. Defaults to ``False`` when
        the adapter is unknown or doesn't expose the flag.
        FNauthorization_is_upstreamrB   rU   r;   r=   r3   r5   rO   s       r   "_adapter_authorization_is_upstreamz<GatewayAuthorizationMixin._adapter_authorization_is_upstream   sK    "  	5--h@@?5GG%@%HHIIIr'   c               z    |sdS |                      ||          }|dS t          t          |dd                    S )a  Whether the adapter for *platform* gates access at intake itself.

        Mirrors ``BasePlatformAdapter.enforces_own_access_policy``. Adapters
        such as WeCom, Weixin, Yuanbao, QQBot, and WhatsApp evaluate their
        documented ``dm_policy`` / ``group_policy`` / ``allow_from`` config before a
        message is dispatched to the gateway. The flag alone is NOT "already
        authorized": these adapters default to ``open``, which forwards every
        sender, so ``_is_user_authorized`` only trusts the adapter when its
        effective policy for the chat type is an actual ``allowlist`` restriction
        (see that method). Defaults to ``False`` when the adapter is unknown or
        doesn't expose the flag.
        FNenforces_own_access_policyrX   rY   s       r   #_adapter_enforces_own_access_policyz=GatewayAuthorizationMixin._adapter_enforces_own_access_policy   sM    $  	5 --h@@?5GG%A5IIJJJr'   r   c                  |sdS |                      ||          }|t          |dd          nd}|~t          | dd          }|*t          |d          r|j                            |          nd}|rt          |dd          nd}t          |t                    r|                    d          }t          |pd                                          	                                S )u  Best-effort read of an own-policy adapter's effective DM policy.

        Returns the lowercased ``dm_policy`` (``"open"`` / ``"allowlist"`` /
        ``"disabled"`` / ``"pairing"``) for *platform*, or ``""`` when unknown.
        Prefers the live adapter's resolved ``_dm_policy`` — which already folds
        in both ``config.extra`` and the ``<PLATFORM>_DM_POLICY`` env var (the
        env var is not always bridged back into ``config.extra``) — and falls
        back to ``config.extra`` for bare runners built without a live adapter.

        Used by ``_is_user_authorized`` to decide whether an own-policy adapter
        actually restricted DM senders to a configured allowlist (trustworthy)
        or merely forwarded everyone under ``dm_policy: open`` / for a pairing
        handshake (not authorization). "Reached the gateway" only carries an
        authorization signal in the ``allowlist`` case.
        r	   N
_dm_policyconfig	platformsextra	dm_policy
rB   r;   hasattrra   r   r,   dictr   r   lowerr=   r3   r5   rO   policyr`   platform_cfgrb   s           r   _adapter_dm_policyz,GatewayAuthorizationMixin._adapter_dm_policy   s    *  	2--h@@9@9L,555RV>T8T22F %'&+*F*F%  $$X... 
 =IRGL'4888dE%&& 0;//6<R  &&((..000r'   c                  |sdS |                      ||          }|t          |dd          nd}|~t          | dd          }|*t          |d          r|j                            |          nd}|rt          |dd          nd}t          |t                    r|                    d          }t          |pd                                          	                                S )a  Best-effort read of an own-policy adapter's effective group policy.

        Mirror of ``_adapter_dm_policy`` for group / forum / channel traffic:
        returns the lowercased ``group_policy`` (``"open"`` / ``"allowlist"`` /
        ``"disabled"``) for *platform*, or ``""`` when unknown. Prefers the live
        adapter's resolved ``_group_policy`` and falls back to ``config.extra``
        for bare runners built without a live adapter.

        Used by ``_is_user_authorized`` to decide whether an own-policy adapter
        restricted group senders to a configured allowlist (trustworthy) or
        forwarded the whole channel under ``group_policy: open`` (not
        authorization).
        r	   N_group_policyr`   ra   rb   group_policyrd   rh   s           r   _adapter_group_policyz/GatewayAuthorizationMixin._adapter_group_policy  s    &  	2--h@@<C<O/4888UY>T8T22F %'&+*F*F%  $$X... 
 =IRGL'4888dE%&& 3>226<R  &&((..000r'   chat_idc                  |r|sdS |                      ||          }|t          |dd          nd}|~t          | dd          }|*t          |d          r|j                            |          nd}|rt          |dd          nd}t          |t                    r|                    d          }t          |t                    sdS t          |          }	|                    |	          }
t          |
t                    st|	                                }|	                                D ]K\  }}t          |t                    r1|                                |k    rt          |t                    r|}
 nLt          |
t                    s|                    d          }
t          |
t                    sdS |
                    d	          p|
                    d
          }t          |t                    r!t          |                                          S t          |t          t          t          f          rt          d |D                       S dS )a  Whether a per-group sender allowlist gated this group message.

        WeCom supports ``groups.<group_id>.allow_from`` on top of the top-level
        ``group_policy``. A group may be open at the chat level while still
        restricting which senders inside that group can invoke Hermes. If such a
        message reached the gateway, the adapter already checked that sender
        allowlist, so it is a trustworthy intake decision rather than the
        fail-open ``group_policy: open`` case.
        FN_groupsr`   ra   rb   groups*
allow_from	allowFromc              3  X   K   | ]%}t          |                                          V  &d S Nr"   )r$   items     r   	<genexpr>zPGatewayAuthorizationMixin._adapter_group_has_sender_allowlist.<locals>.<genexpr>p  s2      BBTs4yy((BBBBBBr'   )rB   r;   re   ra   r   r,   rf   r   rg   rR   rU   r   r-   tupler+   any)r=   r3   rp   r5   rO   rs   r`   rj   rb   chat_id_str	group_cfgloweredkeyvaluesender_allows                  r   #_adapter_group_has_sender_allowlistz=GatewayAuthorizationMixin._adapter_group_has_sender_allowlist>  sJ      	w 	5--h@@6=6I)T222t>T8T22F %'&+*F*F%  $$X... 
 =IRGL'4888dE%&& -8,,&$'' 	5'llJJ{++	)T** 	!''))G$llnn  
Uc3'' CIIKK7,B,BzRWY]G^G^,B %IE)T** 	(

3I)T** 	5 }}\22PimmK6P6PlC(( 	.**,,---lT5#$677 	CBB\BBBBBBur'   'SessionSource'c                    t          | dd          pi }t          |dd          }|r||v r||         S t          | dd          S )a  Pick the per-profile PairingStore for a source, falling back to global.

        In a multiplexing gateway, each profile owns its own pairing whitelist
        so isolation is preserved. When the source has no profile (single-
        profile gateway, or a path that hasn't stamped profile yet) or the
        profile isn't registered, fall back to ``self.pairing_store`` (the
        global default) so existing behavior is preserved.
        pairing_storesNr5   pairing_store)r;   )r=   rC   per_profiler5   s       r   _pairing_store_forz,GatewayAuthorizationMixin._pairing_store_fors  s[     d$4d;;Ar&)T22 	(w+--w''t_d333r'   c                   ddl m} |j        t          j        t          j        hv rdS |                     |          }|j        du s|                     |j        |          rdS |j	        }|j
        dv r|j        rt          j        dt          j        di                    |j        d          }|r?t          |          }|r.d	 |                    d
          D             }d|v s	|j        |v rdS 	 |                     |          }|Wt%          t%          |dd          dd          pi }	|	                    d          }
|
rt'          |
          }d|v s	|j        |v rdS n# t(          $ r Y nw xY wt          j        dt          j        dt          j        dt          j        di}t%          |dd          rT|                    |j                  }|r8t          |d                                                                          dv rdS |sdS i t          j        dt          j        dt          j        dt          j        dt          j        dt          j        dt          j        dt          j        dt          j        d t          j         d!t          j!        d"t          j        d#t          j"        d$t          j#        d%t          j$        d&t          j%        d't          j        d(t          j&        d)i}t          j        d*i}t          j        dt          j        di}i t          j        d+t          j        d,t          j        d-t          j        d.t          j        d/t          j        d0t          j        d1t          j        d2t          j        d3t          j         d4t          j!        d5t          j        d6t          j"        d7t          j#        d8t          j$        d9t          j%        d:t          j        d;t          j&        d<i}|j        |vre	 dd=l'm(} |                    |j        j)                  }|r,|j*        r|j*        ||j        <   |j+        r|j+        ||j        <   n# t(          $ r Y nw xY w|                    |j        d          }|r%tY          |                                          d>v rdS t%          |d?d          du rdS |j        r|j        j)        nd}| -                    |          }||.                    ||          rdS tY          |                    |j        d                    }d}d}|j
        d@v rPtY          |                    |j        d                    }tY          |                    |j        d                    }tY          dA          }|s|s|s|s| /                    |j        |          r|j
        dv rA| 0                    |j        |          }| 1                    |j        |j        |          rdS n| 2                    |j        |          }|dBk    rc|j
        dvrX| 3                    |j        |          }|t%          |dCd          nd}ti          |          rtk           ||                    S dS |                     |          }|qt%          t%          |dd          dd          pi }	|j
        dv r|	                    dD          }n|	                    dE          }|rt'          |          }||v sd|v rdS tY          dF                                          d>v S |r>|j
        d@v r5|j        r.dG |                    d
          D             }d|v s	|j        |v rdS |j        t          j        k    r|r|j
        d@v r|j        rzdH |                    d
          D             }|rYt%          | dId          s=|6                    dJd
7                    tq          |                               d| _9        |j        |v rdS tu                      }|r2|;                    dK |                    d
          D                        |r2|;                    dL |                    d
          D                        |r2|;                    dM |                    d
          D                        d|v rdS |h} dN|v r.| <                    |                    dN          d                    |j        t          j        t          j        hv rtu                      }!|D ]$}"|!;                    t{          |"                     %|!r|!}| ;                    t{          |                     t}          |          }#|#r| <                    |#           |j        1|j        j)        dOk    r!|j?        r| <                    |j?                   tk          | |z            S )Pao  
        Check if a user is authorized to use the bot.
        
        Checks in order:
        1. Per-platform allow-all flag (e.g., DISCORD_ALLOW_ALL_USERS=true)
        2. Environment variable allowlists (TELEGRAM_ALLOWED_USERS, etc.)
        3. DM pairing approved list
        4. Global allow-all (GATEWAY_ALLOW_ALL_USERS=true)
        5. Default: deny
        r   )loggerTrT   >   forumgroupchannelTELEGRAM_GROUP_ALLOWED_CHATSQQ_GROUP_ALLOWED_USERSr	   c                ^    h | ]*}|                                 |                                 +S r!   r)   )r$   cids     r   r&   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<setcomp>  s>     ) ) )99;;)		) ) )r'   r*   rt   Nr`   rb   group_allowed_chatsDISCORD_ALLOW_BOTSFEISHU_ALLOW_BOTSTELEGRAM_ALLOW_BOTSSLACK_ALLOW_BOTSis_botFnone>   allmentionsTELEGRAM_ALLOWED_USERSDISCORD_ALLOWED_USERSWHATSAPP_ALLOWED_USERSWHATSAPP_CLOUD_ALLOWED_USERSSLACK_ALLOWED_USERSSIGNAL_ALLOWED_USERSEMAIL_ALLOWED_USERSSMS_ALLOWED_USERSMATTERMOST_ALLOWED_USERSMATRIX_ALLOWED_USERSDINGTALK_ALLOWED_USERSFEISHU_ALLOWED_USERSWECOM_ALLOWED_USERSWECOM_CALLBACK_ALLOWED_USERSWEIXIN_ALLOWED_USERSBLUEBUBBLES_ALLOWED_USERSQQ_ALLOWED_USERSYUANBAO_ALLOWED_USERSTELEGRAM_GROUP_ALLOWED_USERSTELEGRAM_ALLOW_ALL_USERSDISCORD_ALLOW_ALL_USERSWHATSAPP_ALLOW_ALL_USERSWHATSAPP_CLOUD_ALLOW_ALL_USERSSLACK_ALLOW_ALL_USERSSIGNAL_ALLOW_ALL_USERSEMAIL_ALLOW_ALL_USERSSMS_ALLOW_ALL_USERSMATTERMOST_ALLOW_ALL_USERSMATRIX_ALLOW_ALL_USERSDINGTALK_ALLOW_ALL_USERSFEISHU_ALLOW_ALL_USERSWECOM_ALLOW_ALL_USERSWECOM_CALLBACK_ALLOW_ALL_USERSWEIXIN_ALLOW_ALL_USERSBLUEBUBBLES_ALLOW_ALL_USERSQQ_ALLOW_ALL_USERSYUANBAO_ALLOW_ALL_USERS)platform_registry>   1yestruerole_authorized>   r   r   GATEWAY_ALLOWED_USERS	allowlist_is_dm_allowedgroup_allow_fromru   GATEWAY_ALLOW_ALL_USERSc                ^    h | ]*}|                                 |                                 +S r!   r)   )r$   rp   s     r   r&   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<setcomp>  sB     ! ! !$+RYR_R_RaRa!! ! !r'   c                    h | ]=}|                                                     d           )|                                 >S )-)r   
startswith)r$   vs     r   r&   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<setcomp>  sL       7799'',,		  r'   #_warned_telegram_group_users_legacyu   TELEGRAM_GROUP_ALLOWED_USERS contains chat-ID-shaped values (%s). Treating them as chat IDs for backward compatibility. Move chat IDs to TELEGRAM_GROUP_ALLOWED_CHATS — the _USERS var is now for sender user IDs.c              3  f   K   | ],}|                                 |                                 V  -d S rx   r)   r$   uids     r   rz   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<genexpr>  s<      ccsWZW`W`WbWbcsyy{{ccccccr'   c              3  f   K   | ],}|                                 |                                 V  -d S rx   r)   r   s     r   rz   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<genexpr>  s<      eesY\YbYbYdYdesyy{{eeeeeer'   c              3  f   K   | ],}|                                 |                                 V  -d S rx   r)   r   s     r   rz   z@GatewayAuthorizationMixin._is_user_authorized.<locals>.<genexpr>  s<      aasUXU^U^U`U`asyy{{aaaaaar'   @simplex)@gateway.runr   r3   r   HOMEASSISTANTWEBHOOKrS   rF   rZ   user_id	chat_typerp   TELEGRAMQQBOTr   r   r.   rJ   r;   r0   r   DISCORDFEISHUSLACKrg   r   WHATSAPPWHATSAPP_CLOUDSIGNALEMAILSMS
MATTERMOSTMATRIXDINGTALKWECOMWECOM_CALLBACKWEIXINBLUEBUBBLESYUANBAOgateway.platform_registryr   r   allowed_users_envallow_all_envr   r   is_approvedr]   ro   r   rk   rB   r<   rU   warningjoinsortedr   r+   updateadd_expand_whatsapp_auth_aliases_normalize_whatsapp_identifier	user_name)$r=   rC   r   adapter_profiler   chat_allowlist_envraw_chat_allowlistallowed_group_idsrO   rb   adapter_group_allowedallowedplatform_allow_bots_mapallow_bots_varplatform_env_mapplatform_group_user_env_mapplatform_group_chat_env_mapplatform_allow_all_mapr   entryplatform_allow_all_varplatform_namer   platform_allowlistgroup_user_allowlistgroup_chat_allowlistglobal_allowlisteffective_policydm_checkadapter_allowlegacy_chat_idsallowed_ids	check_idsnormalized_allowed_ids
allowed_idnormalized_user_ids$                                       r   _is_user_authorizedz-GatewayAuthorizationMixin._is_user_authorized  s?    	'&&&&& ?x5x7GHHH4::6BB: .$66$:a:aO# ;b ;
 ;
6 4. <<<<!#A 8" c&/2&&  " 	$%78J%K%K"% $) )#5#;#;C#@#@) ) )%
 ///6>EV3V3V#t
226::&#GGXt$D$DgtTTZXZE,1II6K,L,L), ("34I"J"J'>>V^w-F-F#'4    2O04N.	#
 68U++ 	488IIN "4^V"L"L"R"R"T"T"Z"Z"\"\`s"s"st 	5
7
5
 7
 #%C	

 N1
 O3
 N1
 L-
 !;
 O3
 7
 O3
 N1
 #%C
 O3
   "=!
" N.#
$ 5%
 
* ='
# =N4'
#"
9"
7"
 9"
 #%E	"

 N3"
 O5"
 N3"
 L/"
 !="
 O5"
 9"
 O5"
 N3"
 #%E"
 O5"
   "?!"
" N0#"
$ 7%"
 "
, ?"222	GGGGGG)--fo.CDD V. T<A<S(9* VBGBU.v?    "8!;!;FOR!P!P! 	i0F&G&G&M&M&O&OSg&g&g4 6,e44<<4  28H--b//77$)B)B=RY)Z)Z$4 ''7';';FOR'P'PQQ!!111#,-H-L-LV_^`-a-a#b#b #,-H-L-LV_^`-a-a#b#b $%<==! R	X*> R	XG[ R	Xdt R	X0 77' 8   )  #'DDD'+'A'A / (B ( ($ ?? / @   $
  $t$ (,'>'> / (? ( ($ ${22 '/LLL"&"="="O$3 #> # #  '2 $G-=tDDD!% !
 $H-- ;#'(9(9#:#::4
 ..v66G"4 @ @'4PPVTV#'DDD$)II.@$A$AMM$)IIl$;$;M  $/>>G'))SG^^#t677==??CWWW
   	F$48J$J$Jv~$J! !/C/I/I#/N/N! ! ! '''6>=N+N+Nt Ox000$ 1 $666 7 -33C88  O
   t%JERR DNN6 !8!899   @DD<>_444 ee 	dcc6H6N6Ns6S6Scccccc 	fee6J6P6PQT6U6Ueeeeee 	baa6F6L6LS6Q6Qaaaaaa +4I	'>>MM'--,,Q/000 ?x0(2IJJJ%(UU") Y Y
&--.KJ.W.WXXXX% 54:7CCDDD!?!H!H! 20111 O'%22  3 MM&*+++I+,,,s&   0A,E 
E,+E,AQ* *
Q76Q7c                  t          | dd          }|rht          |d          rX|rVt          |d          r|j                            |          nd}|r(dt          |di           v r|                    |          S |t
          j        k    rdS |r"t          |d          r|j        dk    r|j        S |r|                     ||	          }|s|rt          |d          r|j                            |          }|rt          |dd          nd}t          |t                    rHt          |                    d
          pd                                                                          }|dk    rdS |dv rdS |rmi t
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        dt
          j        d}t
          j        dt
          j        d i}t=          |                    |d                                                    rdS |                    |d!          D ]&}	t=          |	                                          r dS 't=          d"                                          rdS dS )#u}  Return how unauthorized DMs should be handled for a platform.

        Resolution order:
        1. Explicit per-platform ``unauthorized_dm_behavior`` in config — always wins.
        2. Email defaults to ``"ignore"`` unless explicitly opted into
           pairing. Inboxes may contain arbitrary unread human messages, so
           replying with pairing codes is not a safe platform default.
        3. Explicit global ``unauthorized_dm_behavior`` in config — wins for
           chat-shaped platforms when no per-platform override is set.
        4. When an adapter-level DM policy opts into pairing or silent drop, honor it.
        5. When an allowlist (``PLATFORM_ALLOWED_USERS``,
           ``PLATFORM_GROUP_ALLOWED_USERS`` / ``PLATFORM_GROUP_ALLOWED_CHATS``,
           or ``GATEWAY_ALLOWED_USERS``) is configured, default to ``"ignore"`` —
           the allowlist signals that the owner has deliberately restricted
           access; spamming unknown contacts with pairing codes is both noisy
           and a potential info-leak. (#9337)
        6. No allowlist and no explicit config → ``"pair"`` (open-gateway default).
        r`   Nget_unauthorized_dm_behaviorra   unauthorized_dm_behaviorrb   ignorepairrT   rc   r	   pairing>   disabledr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )r   r   )r   r!   r   )r;   re   ra   r   r  r   r   r  rk   r,   rf   r   r   rg   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )
r=   r3   r5   r`   rj   rc   rb   r  platform_group_env_mapenv_keys
             r   _get_unauthorized_dm_behaviorz7GatewayAuthorizationMixin._get_unauthorized_dm_behavior  s   0 x..  	Egf&DEE 	E( 	E=DV[=Y=Yc6+//999_cL E :glT[]_>`>` ` `::8DDD x~%%8  	7gf&@AA 	7.&8866  
	 //'/JJI R RGFK,H,H R%/33H==@LVgt<<<RVeT** R #EIIk$:$:$@b A A G G I I O O Q QII%%v555x
  	$ !#;  #:  !#;  ')G	 
 #8  #9  #8  #6  #%?  #9  !#;  #9  #8  ')G  #9   $&A! " #5# ( ! $  ;&" ""2"6"6x"D"DEEKKMM  x155hCC $ $%g..4466 $#88$ 566<<>> 	8vr'   rx   )r3   r4   r5   r6   )rC   rD   )rC   r   )rC   r   r   r6   )r3   r4   r5   r6   r   rU   )r3   r4   r5   r6   r   r   )r3   r4   rp   r6   r5   r6   r   rU   )rC   r   )rC   r   r   rU   )__name__
__module____qualname____doc__rB   rJ   rG   rS   rZ   r]   rk   ro   r   r   r  r!  r!   r'   r   r2   r2   Z   s       @@
 "&#& #& #& #& #&J
 
 
 
6   .0 0 0 0$ "&	J J J J J J8 "&	K K K K K K@ "&	#1 #1 #1 #1 #1 #1R "&	!1 !1 !1 !1 !1 !1P "&3 3 3 3 3 3j4 4 4 4M- M- M- M-f "&	g g g g g g g gr'   r2   )r	   )r
   r   r   r   r   r   )r   r   )r%  
__future__r   r   typingr   gateway.configr   gateway.sessionr   gateway.whatsapp_identityr   r   r   r   r   r   r0   r2   r!   r'   r   <module>r+     s   " # " " " " " 				       # # # # # # ) ) ) ) ) )       0 0 0 0 00 0 0 0 0:J J J J^ ^ ^ ^ ^ ^ ^ ^ ^ ^r'   