
    `gj2D                    J   d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	 ddl
mZ ddlmZmZmZmZmZ 	 ddlmZ dZdd
lmZmZ ddlmZmZmZmZmZ  ej>                  e       Z!dZ"dZ#dZ$dZ%dZ&eee'ef   eged   dz  f   Z(ddZ) G d de      Z*y# e$ r d	ZdZY aw xY w)z@Microsoft Graph webhook adapter for change-notification ingress.    )annotationsN)deque)sha1)Any	AwaitableCallableDictOptional)webTF)PlatformPlatformConfig)BasePlatformAdapterMessageEventMessageType
SendResultis_network_accessiblez0.0.0.0i!  z/msgraph/webhooki  i   c                     t         S )z;Return whether required webhook dependencies are available.)AIOHTTP_AVAILABLE     T/root/.hermes/venv/lib/python3.12/site-packages/gateway/platforms/msgraph_webhook.py"check_msgraph_webhook_requirementsr   )   s    r   c                  L    e Zd ZdZd fdZedd       Zedd       Zedd       Zed d       Z	e	 	 	 	 d!d       Z
d"dZd#d	Zd
dd$dZd%dZ	 	 d&	 	 	 	 	 	 	 	 	 d'dZd(dZd)dZd)dZd)dZd*dZd+dZd,dZd-dZd.dZ	 	 	 	 	 	 d/dZd0dZd1dZ	 	 	 	 	 	 d2dZ xZS )3MSGraphWebhookAdapterzIReceive Microsoft Graph change notifications and surface them internally.c           	        t         |   |t        j                         |j                  xs i }t        |j                  dt                    | _        t        |j                  dt                    | _        | j                  |j                  dt                    | _        | j                  |j                  dd            | _        |j                  d      xs g D cg c]4  }t        |      j!                         rt        |      j!                         6 c}| _        | j%                  |j                  d            | _        t)        dt        |j                  d	t*                          | _        t)        dt        |j                  d
t.                          | _        | j3                  |j                  d            | _        d | _        d | _        t;               | _        t?               | _         d| _!        d| _"        y c c}w )Nhostportwebhook_pathhealth_pathz/healthaccepted_resourcesclient_state   max_seen_receiptsmax_body_bytesallowed_source_cidrsr   )#super__init__r   MSGRAPH_WEBHOOKextrastrgetDEFAULT_HOST_hostintDEFAULT_PORT_port_normalize_pathDEFAULT_WEBHOOK_PATH_webhook_path_health_pathstrip_accepted_resources_string_or_none_client_statemaxDEFAULT_MAX_SEEN_RECEIPTS_max_seen_receiptsDEFAULT_MAX_BODY_BYTES_max_body_bytes_parse_allowed_source_cidrs_allowed_source_networks_runner_notification_schedulerset_seen_receiptsr   _seen_receipt_order_accepted_count_duplicate_count)selfconfigr)   value	__class__s       r   r'   zMSGraphWebhookAdapter.__init__1   s   !9!9:"eii=>
eii=>
"&"6"6IIn&:;#
 "&!5!5eiiy6Y!Z  ))$89?R/
5z! J/
 
 -1,@,@>AZ,["%s59902KLM#
  #s599-/EFG 
 ,,UYY7M-NO 	% HL$(+/4w   !)/
s   9Hc                F    | y t        |       j                         }|xs d S Nr*   r5   )rI   texts     r   r7   z%MSGraphWebhookAdapter._string_or_noneP   s&    =5z!|tr   c                t    t        | xs d      j                         xs d}|j                  d      r|S d| S N /)r*   r5   
startswith)pathraws     r   r1   z%MSGraphWebhookAdapter._normalize_pathW   s9    $*"o##%,nnS)s83%y8r   c                j    t        | j                  d      xs d      j                         }|rd| S y )NidrQ   zid:)r*   r+   r5   )notificationexplicit_ids     r   _build_receipt_keyz(MSGraphWebhookAdapter._build_receipt_key\   s8    ,**406B7==?&&r   c                Z    t        | xs d      j                         j                  d      S rP   rM   )resources    r   _normalize_resource_valuez/MSGraphWebhookAdapter._normalize_resource_valuec   s%    8>r"((*0055r   c                   | g S t        | t              r-| j                  d      D cg c]  }|j                          }}nDt        | t        t
        t        f      r'| D cg c]  }t        |      j                          }}ng S g }|D ],  }|s	 |j                  t        j                  |d             . |S c c}w c c}w # t        $ r t        j                  d|       Y \w xY w)a  Parse an optional list of CIDR ranges allowed to POST to the webhook.

        An empty or missing value means "allow everything" (same behavior as
        before this field existed). When populated, requests from source IPs
        outside every listed CIDR are rejected with 403 before the body is
        parsed. Use this to restrict the endpoint to Microsoft Graph's
        published webhook source ranges in production deployments.
        ,F)strictzA[msgraph_webhook] Ignoring invalid allowed_source_cidrs entry: %r)
isinstancer*   splitr5   listtuplerB   append	ipaddress
ip_network
ValueErrorloggerwarning)rU   chunk
candidatesnetworkss       r   r>   z1MSGraphWebhookAdapter._parse_allowed_source_cidrsg   s     ;Ic358YYs^DE%++-DJDdE3/0:=>#e***,>J>I13 		E	 4 4U5 IJ			 # E>  Ws   B;! C &CC'&C'c                    || _         y rL   )rA   )rG   	schedulers     r   set_notification_schedulerz0MSGraphWebhookAdapter.set_notification_scheduler   s
    '0$r   c                J    t        | j                        xr | j                   S rL   )r   r-   r?   rG   s    r   &_source_allowlist_required_but_missingz<MSGraphWebhookAdapter._source_allowlist_required_but_missing   s    $TZZ0V9V9V5VVr   F)is_reconnectc                 K   | j                   t        j                  d       y| j                         r!t        j                  d| j                         yt        j                  | j                        }|j                  j                  | j                  | j                         |j                  j                  | j                  | j                         |j                  j                  | j                  | j                         t        j                   |      | _        | j"                  j%                          d {    t        j&                  | j"                  | j                  | j(                        }|j+                          d {    | j-                          t        j/                  d| j                  | j(                  | j                         y7 7 Mw)NzI[msgraph_webhook] Refusing to start without extra.client_state configuredFz[msgraph_webhook] Refusing to start: binding to %s requires extra.allowed_source_cidrs. Configure the Microsoft Graph source CIDRs or bind to loopback (127.0.0.1/::1) behind a tunnel or reverse proxy.)client_max_sizez&[msgraph_webhook] Listening on %s:%d%sT)r8   ri   errorrs   r-   r   Applicationr=   routeradd_getr4   _handle_healthr3   _handle_validationadd_post_handle_notification	AppRunnerr@   setupTCPSiter0   start_mark_connectedinfo)rG   rt   appsites       r   connectzMSGraphWebhookAdapter.connect   sX    %LL[ 668LL+ 

 ood.B.BC

4,,d.A.AB

4--t/F/FG

D..0I0IJ}}S)ll  """{{4<<TZZ@jjl4JJJJ		
  	#s&   D;G=G>AG
GAGGc                   K   | j                   )| j                   j                          d {    d | _         | j                          y 7 wrL   )r@   cleanup_mark_disconnectedrr   s    r   
disconnectz MSGraphWebhookAdapter.disconnect   s>     <<#,,&&(((DL! )s   *AA	Ac                V   K   t         j                  d||d d        t        d      S w)Nz%[msgraph_webhook] Response for %s: %s   T)success)ri   r   r   )rG   chat_idcontentreply_tometadatas        r   sendzMSGraphWebhookAdapter.send   s*      	;WgdsmT$''s   ')c                   K   |ddS w)Nwebhook)nametyper   )rG   r   s     r   get_chat_infoz#MSGraphWebhookAdapter.get_chat_info   s     33s   c                   K   | j                  |      st        j                  d      S t        j                  d| j                  j
                  | j                  | j                  | j                  d      S w)N  statusok)r   platformr   accepted
duplicates)	_source_ip_allowedr   Responsejson_responser   rI   r3   rE   rF   )rG   requests     r   r{   z$MSGraphWebhookAdapter._handle_health   sf     &&w/<<s++   MM// $ 2 2 00"33
 	
s   A5A7c                   K   | j                  |      st        j                  d      S |j                  j	                  dd      }|st        j                  d      S t        j                  |d      S w)a  Handle Microsoft Graph subscription validation handshake.

        Graph validates a subscription endpoint by sending a GET with
        ``validationToken`` in the query string; the service must echo the
        token verbatim as ``text/plain`` within 10 seconds. Anything else
        (bare GET, GET without the token) is rejected so the endpoint can't
        be enumerated or mistakenly used for data exfiltration.
        r   r   validationTokenrQ     
text/plainrN   content_type)r   r   r   queryr+   )rG   r   validation_tokens      r   r|   z(MSGraphWebhookAdapter._handle_validation   sa      &&w/<<s++"==,,->C<<s++||!1MMs   A3A5c                >  K   | j                  |      st        j                  d      S |j                  j	                  dd      }|rt        j                  |d      S 	 |j
                  }|%|| j                  kD  rt        j                  d      S 	 |j                          d {   }t        |      | j                  kD  rt        j                  d      S 	 t        j                  |j                  d	            }t        |t               st        j                  d      S |j	                  d
      }t        |t"              st        j                  d      S d}d}d}	d}
|D ]  }t        |t               s|
dz  }
t!        |      }| j%                  t'        |j	                  d      xs d            s|
dz  }
W| j)                  |      s|	dz  }	n| j+                  |      }|(| j-                  |      r|dz  }| j/                  |       |dz  }| xj0                  dz  c_        | j3                  ||      }| j5                  ||        | xj6                  |z  c_        |s|rt        j                  d      S |	r|
st        j                  d      S t        j                  d      S # t        $ r d }Y Pw xY w7 # t        $ r t        j                  d      cY S w xY w# t        j                  t        f$ r t        j                  d      cY S w xY ww)Nr   r   r   rQ   r   r   i  r   utf-8rI   r   r"   r\      )r   r   r   r   r+   content_length	Exceptionr=   readlenjsonloadsdecodeJSONDecodeErrorUnicodeDecodeErrorra   dictrc   _resource_acceptedr*   _verify_client_staterZ   _has_seen_receipt_remember_receiptrE   _build_message_event_schedule_notificationrF   )rG   r   r   r   raw_bodybodynotificationsr   r   auth_rejectedother_rejectedraw_notificationrX   receipt_keyevents                  r   r~   z*MSGraphWebhookAdapter._handle_notification   s    &&w/<<s++ #==,,->C<<%5LQQ	"$33N %.4;O;O*O<<s++	,$\\^+H x=4///<<s++	,::hoog67D $%<<s++)-.<<s++
 - 	=.5!# 01L**3|/?/?
/K/Qr+RS!#,,\:
 "11,?K&))+6!OJ&&{3MH  A% --lKHE''e<5	=8 	+ z<<s++<<s++||3''G  	"!N	" , 	,<<s++	, $$&89 	,<<s++	,s   AL J. ,'LK 'K (K ,.L$K( ?F/L.J=9L<J==L K K%"L$K%%L(/LLLLc                    | j                         ry| j                  sy|j                  xs d}|sy	 t        j                  |      t        fd| j                  D              S # t
        $ r Y yw xY w)a  Return True if the request's source IP is in the configured allowlist.

        Loopback-only binds may omit ``allowed_source_cidrs`` for local reverse
        proxies and dev tunnels. Network-accessible binds fail closed until an
        explicit CIDR allowlist is configured.
        FTrQ   c              3  &   K   | ]  }|v  
 y wrL   r   ).0network	peer_addrs     r   	<genexpr>z;MSGraphWebhookAdapter._source_ip_allowed.<locals>.<genexpr>D  s     UG9'Us   )rs   r?   remoterf   
ip_addressrh   any)rG   r   peerr   s      @r   r   z(MSGraphWebhookAdapter._source_ip_allowed2  sv     668,,~~#	!,,T2I Ut7T7TUUU  		s   A' '	A32A3c                B   | j                   sy| j                  |      }| j                   D ]r  }| j                  |      }|s|j                  d      r0|d d j                  d      }||k(  s|j	                  | d      r yX||k(  s|j	                  | d      sr y y)NT*rR   F)r6   r]   endswithrstriprS   )rG   r\   normalized_resourcepatternnormalized_patternprefixs         r   r   z(MSGraphWebhookAdapter._resource_acceptedF  s    ''"<<XF// 	G!%!?!?!H%!**3/+CR077<&&04G4R4RV\U]]^S_4`#'99&115G4H2JK	 r   c                    | j                   }|y| j                  |j                  d            }|yt        j                  |j                         |j                               S )a  Verify the Graph-supplied clientState matches the configured secret.

        Uses ``hmac.compare_digest`` instead of ``==`` so that a mismatch
        doesn't leak how many leading characters matched via string-compare
        timing. The configured client_state is a shared secret (documented in
        the setup guide as "generate with ``openssl rand -hex 32``"), so a
        timing-safe compare is the right primitive.
        FclientState)r8   r7   r+   hmaccompare_digestencode)rG   rX   expectedprovideds       r   r   z*MSGraphWebhookAdapter._verify_client_stateZ  s_     %%''(8(8(GH ""8??#4hoo6GHHr   c                    || j                   v S rL   )rC   )rG   r   s     r   r   z'MSGraphWebhookAdapter._has_seen_receiptm  s    d1111r   c                f   | j                   j                  |       | j                  j                  |       t	        | j                        | j
                  kD  rY| j                  j                         }| j                   j                  |       t	        | j                        | j
                  kD  rXy y rL   )rC   addrD   re   r   r;   popleftdiscard)rG   r   oldests      r   r   z'MSGraphWebhookAdapter._remember_receiptp  s    ,  ''4$**+d.E.EE--557F''/ $**+d.E.EEr   c                8   |xs@ dt        t        j                  |d      j                  d            j	                          }| j                  d|j                  dd       dd	d
d      }t        | j                  |      t        j                  |||d      S )Nzsha1:T	sort_keysr   zmsgraph:subscriptionIdunknownzmsgraph/webhookr   msgraphzMicrosoft Graph)r   	chat_name	chat_typeuser_id	user_name)rN   message_typesourceraw_message
message_idinternal)r   r   dumpsr   	hexdigestbuild_sourcer+   r   _render_promptr   TEXT)rG   rX   r   r   r   s        r   r   z*MSGraphWebhookAdapter._build_message_eventw  s    
 !xeDLTX1Y1`1`ah1i,j,t,t,v+w$x
""|//0@)LMN'' # 
 $$\2$))$!
 	
r   c                &   | j                   j                  j                  dd      }|rI||j                  dd      |j                  dd      |j                  dd      d}| j                  ||      S t	        j
                  |dd	      d d
 }d| dS )NpromptrQ   r\   
changeTyper   )rX   r\   change_typesubscription_id   T)indentr   i  z.Microsoft Graph change notification:

```json
z
```)rH   r)   r+   _render_templater   r   )rG   rX   templatepayloadrendereds        r   r   z$MSGraphWebhookAdapter._render_prompt  s    ;;$$((26 ,(,,Z<+//bA#/#3#34Db#I	G ((7;;::l1EetLB8*ERRr   c                >    dd l }dfd}|j                  d||      S )Nr   c                (   | j                  d      }}|j                  d      D ]0  }t        |t              r|j	                  |d| d      }*d| dc S  t        |t        t
        f      rt        j                  |d      d d S t        |      S )Nr"   .{}Tr   i  )	grouprb   ra   r   r+   rc   r   r   r*   )matchkeyrI   partr  s       r   _resolvez8MSGraphWebhookAdapter._render_template.<locals>._resolve  s    ++a.C E		# (eT*!IIdbRL9EuB<'	(
 %$.zz%48$??u:r   z\{([a-zA-Z0-9_.]+)\})r  z're.Match[str]'returnr*   )resub)rG   r  r  r  r  s     `  r   r  z&MSGraphWebhookAdapter._render_template  s!    
	 vv-xBBr   c                   | j                   }|t |||      }t        j                  |      rUt        j                  |      }| j                  j                  |       |j                  | j                  j                         y t        j                  | j                  |            }| j                  j                  |       |j                  | j                  j                         y rL   )	rA   asyncioiscoroutinecreate_task_background_tasksr   add_done_callbackr   handle_message)rG   rX   r   ro   resulttasks         r   r   z,MSGraphWebhookAdapter._schedule_notification  s    
 00	 |U3F""6***62&&**40&&t'='='E'EF""4#6#6u#=>""4(t55==>r   )rH   r   )rI   r   r  Optional[str])rT   r   r  r*   )rX   Dict[str, Any]r  r  )r\   r*   r  r*   )rU   r   r  zlist[ipaddress._BaseNetwork])ro   zOptional[NotificationScheduler]r  Noner  bool)rt   r!  r  r!  )r  r  )NN)
r   r*   r   r*   r   r  r   zOptional[Dict[str, Any]]r  r   )r   r*   r  r  )r   'web.Request'r  z'web.Response')r   r"  r  r!  )r\   r*   r  r!  )rX   r  r  r!  )r   r*   r  r!  )r   r*   r  r  )rX   r  r   r  r  r   )rX   r  r  r*   )r  r*   r  r  r  r*   )rX   r  r   r   r  r  )__name__
__module____qualname____doc__r'   staticmethodr7   r1   rZ   r]   r>   rp   rs   r   r   r   r   r{   r|   r~   r   r   r   r   r   r   r   r  r   __classcell__)rJ   s   @r   r   r   .   sX   S">   9 9   6 6 	% B1W 5:  D" #'-1(( (  	(
 +( 
(4
N O(bV((I&20
$
 #
 
	
,SC"?$? ? 
	?r   r   r   )+r&  
__future__r   r  r   rf   r   loggingcollectionsr   hashlibr   typingr   r   r   r	   r
   aiohttpr   r   ImportErrorgateway.configr   r   gateway.platforms.baser   r   r   r   r   	getLoggerr#  ri   r,   r/   r2   r:   r<   r*   NotificationSchedulerr   r   r   r   r   <module>r4     s    F "        ; ;
 4  
		8	$)   "  $sCx.,!?4SWAW!WX 
M?/ M?9  
Cs   B 	B"!B"