
    7j                         d Z ddlm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  ed      ZdZ G d	 d
e      Z G d d      Z G d de      Z G d de      ZddZy)a  Central API client.

The rest of the agent talks to the control plane only through ApiClient, so the
real HTTP implementation can be swapped for the development mock without
touching call sites. Choose the implementation with get_api_client(config):
WOLFPANEL_API_MOCK=1 selects the mock.

Endpoints (frozen v1 contract):
  POST /api/v1/agent/register             (no auth; install token in body)
  POST /api/v1/agent/heartbeat            (X-Agent-Token header)
  POST /api/v1/agent/discovery/sessions   (X-Agent-Token header)
  POST /api/v1/agent/discovery/items      (X-Agent-Token header)
  POST /api/v1/agent/discovery/complete   (X-Agent-Token header)
  POST /api/v1/agent/discovery/failed     (X-Agent-Token header)
  GET  <manifest_url>                     (update check)
    )annotationsN)Any)Config)
get_loggerredactapi   c                  &     e Zd ZdZdd fdZ xZS )ApiErrorzBRaised for non-recoverable API errors (4xx / malformed responses).c                2    t         |   |       || _        y N)super__init__status_code)selfmessager   	__class__s      D/var/www/downloads.wolfpanel.net/public_html/agent/src/api_client.pyr   zApiError.__init__$   s    !&    r   )r   strr   z
int | NonereturnNone)__name__
__module____qualname____doc__r   __classcell__)r   s   @r   r   r   !   s    L' 'r   r   c                  H    e Zd ZdZd
dZddZddZddZddZddZ	ddZ
y	)	ApiClientz0Interface every client implementation satisfies.c                    t         r   NotImplementedErrorr   payloads     r   registerzApiClient.register,       !!r   c                    t         r   r!   r   r$   tokens      r   	heartbeatzApiClient.heartbeat/   r&   r   c                    t         r   r!   r(   s      r   open_discovery_sessionz ApiClient.open_discovery_session2   r&   r   c                    t         r   r!   r(   s      r   upload_discovery_itemsz ApiClient.upload_discovery_items5   r&   r   c                    t         r   r!   r(   s      r   complete_discoveryzApiClient.complete_discovery8   r&   r   c                    t         r   r!   r(   s      r   fail_discoveryzApiClient.fail_discovery;   r&   r   c                    t         r   r!   r   urls     r   fetch_manifestzApiClient.fetch_manifest>   r&   r   Nr$   dict[str, Any]r   r8   r$   r8   r)   
str | Noner   r   r$   r8   r)   r:   r   r8   r5   r   r   r8   r   r   r   r   r%   r*   r,   r.   r0   r2   r6    r   r   r   r   )   s(    :"""""""r   r   c                  X    e Zd Z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y)RealApiClientz?Talks to the live Central API over HTTPS using only the stdlib.c                0    |j                  d      | _        y )N/)rstripbase_url)r   rD   s     r   r   zRealApiClient.__init__E   s     ,r   c                   | j                    | }t        j                  |      j                  d      }ddd}|r||d<   t        j
                  j                  |||d      }	 t        j
                  j                  |t              5 }|j                         j                  d      }	|	rt        j                  |	      ni cd d d        S # 1 sw Y   y xY w# t        j                  j                  $ r,}
t        | d	|
j                   |
j                  
      |
d }
~
wt        j                  j                   t"        f$ r}
t        | d|
       |
d }
~
ww xY w)Nutf-8zapplication/jsonwolfpanel-agent)zContent-Type
User-AgentzX-Agent-TokenPOST)dataheadersmethodtimeoutz	 -> HTTP )r   z -> transport error: )rD   jsondumpsencodeurllibrequestRequesturlopen_TIMEOUTreaddecodeloadserror	HTTPErrorr   codeURLErrorTimeoutError)r   pathr$   r)   r5   rJ   rK   rS   responsebodyexcs              r   _postzRealApiClient._postH   s3   v&zz'"))'2#5EVW
 (-GO$..((4QW(X		I'''B 8h}}--g6+/tzz$'R8 8 8 ||%% 	XdV9SXXJ7SXXNTWW%%|4 	I dV#8>?SH	IsB   %%C 
8C	C CC C E5'D&EEEc                *    | j                  d|d       S )Nz/api/v1/agent/register)r)   rc   r#   s     r   r%   zRealApiClient.register_   s    zz2G4zHHr   c                *    | j                  d||       y )Nz/api/v1/agent/heartbeatre   r(   s      r   r*   zRealApiClient.heartbeatb   s    

,gu=r   c                (    | j                  d||      S )Nz /api/v1/agent/discovery/sessionsre   r(   s      r   r,   z$RealApiClient.open_discovery_sessione       zz<guMMr   c                (    | j                  d||      S )Nz/api/v1/agent/discovery/itemsre   r(   s      r   r.   z$RealApiClient.upload_discovery_itemsh   s    zz97EJJr   c                (    | j                  d||      S )Nz /api/v1/agent/discovery/completere   r(   s      r   r0   z RealApiClient.complete_discoveryk   rh   r   c                (    | j                  d||      S )Nz/api/v1/agent/discovery/failedre   r(   s      r   r2   zRealApiClient.fail_discoveryn   s    zz:GUKKr   c                   t         j                  j                  |ddi      }	 t         j                  j                  |t              5 }t        j                  |j                         j                  d            cd d d        S # 1 sw Y   y xY w# t         j                  j                  t        t        f$ r}t        d|       |d }~ww xY w)NrH   rG   )rK   rM   rF   zmanifest fetch failed: )rR   rS   rT   rU   rV   rO   rY   rW   rX   rZ   r]   r^   
ValueErrorr   )r   r5   rS   r`   rb   s        r   r6   zRealApiClient.fetch_manifestq   s    ..((|EV6W(X	E'''B Chzz(--/"8"8"ABC C C%%|Z@ 	E4SE:;D	Es5   %B 
2B<	B BB B (C:C		CN)rD   r   r   r   )r_   r   r$   r8   r)   r:   r   r8   r7   r9   r;   r<   )r   r   r   r   r   rc   r%   r*   r,   r.   r0   r2   r6   r>   r   r   r@   r@   B   s9    I-I.I>NKNLEr   r@   c                  H    e Zd ZdZd
dZddZddZddZddZddZ	ddZ
y	)MockApiClientzDevelopment stand-in. Logs payloads and returns canned responses.

    Lets the whole agent be exercised end-to-end with no Central API present.
    c                    t         j                  d|j                  d      |j                  d      |j                  d             ddddS )	Nz0[mock] register hostname=%s os=%s fingerprint=%shostnameos_namefingerprintwp_agent_dev_token   active)agent_token	server_idstatusloginfogetr#   s     r   r%   zMockApiClient.register   sJ    >KK
#KK	"KK&		
 0
 	
r   c                    t         j                  d|j                  d      |j                  d      t        |             y )Nz0[mock] heartbeat server_id=%s status=%s token=%srx   ry   )r{   r|   r}   r   r(   s      r   r*   zMockApiClient.heartbeat   s0    >KK$KK!5M		
r   c                v    t         j                  d|j                  d      |j                  d             dddS )Nz8[mock] discovery session open scan_type=%s categories=%s	scan_typeexpected_categoriesru   running
session_idry   rz   r(   s      r   r,   z$MockApiClient.open_discovery_session   s7    FKK$KK-.	

  955r   c                    |j                  dg       }t        j                  d|j                  d      |j                  d      t        |             |j                  d      t        |      t        |      dS )Nitemsz9[mock] discovery items session_id=%s category=%s count=%dr   category)r   accepted
item_count)r}   r{   r|   len)r   r$   r)   r   s       r   r.   z$MockApiClient.upload_discovery_items   sf    GR(GKK%KK
#J		
 "++l3E
e*
 	
r   c                    t         j                  d|j                  d      |j                  d      |j                  d             |j                  d      xs g }|j                  d      |rdddS dddS )	Nz>[mock] discovery complete session_id=%s succeeded=%s failed=%sr   succeeded_categoriesfailed_categoriespartial	completedr   )r   ry   r   rz   )r   r$   r)   faileds       r   r0   z MockApiClient.complete_discovery   s{    LKK%KK./KK+,		
 017R!++l3#)i
 	
/:
 	
r   c                t    t         j                  d|j                  d             |j                  d      ddS )Nz%[mock] discovery failed session_id=%sr   r   r   rz   r(   s      r   r2   zMockApiClient.fail_discovery   s3    3KK%	
 &kk,78LLr   c                :    t         j                  d|       ddddS )Nz[mock] fetch manifest %sz0.1.2zMhttps://agent.wolfpanel.net/releases/0.1.2/wolfpanel-agent-linux-amd64.tar.gz@0000000000000000000000000000000000000000000000000000000000000000)versionr5   sha256)r{   r|   r4   s     r   r6   zMockApiClient.fetch_manifest   s$    +S1b
 	
r   Nr7   r9   r;   r<   r=   r>   r   r   ro   ro   z   s+    


6

M
r   ro   c                    | j                   rt        j                  d       t               S t	        | j
                        S )z4Factory: mock in development, real client otherwise.z0using MockApiClient (WOLFPANEL_API_MOCK enabled))api_mockr{   r|   ro   r@   api_url)configs    r   get_api_clientr      s-    CD((r   )r   r   r   r   )r   
__future__r   rO   urllib.errorrR   urllib.requesttypingr   r   r   loggerr   r   r{   rV   	Exceptionr   r   r@   ro   r   r>   r   r   <module>r      si   " #      %'y '" "25EI 5EpM
I M
`)r   