+
    Nj;                         R t ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIHtHt ^ RIH	t	 R t
]
! 4       t]	! 4       tR tR R ltR tR	 tRR
 R lltRR R lltRR R lltRR R lltRR R lltR# )u  
apikeys.py
Shared API-key store for the Status Checker proxy.

Keys live in one JSON file (default: /etc/status-checker/api_keys.json for the
cloud deployment; falls back to ./api_keys.json for local dev so `launch.py`
works out of the box without root). Override with STATUS_CHECKER_KEYSTORE.

We store only the SHA-256 hash of each key, never the raw key — a leaked
store file doesn't leak usable credentials. The raw key is shown once, at
generation time, and cannot be recovered afterward (only revoked + reissued).
N)datetimetimezone)Lockc                      R p \         P                  P                  R4      '       d   \         P                  R,          # \         P                  P	                  V 4      p\         P                  P                  V4      '       d/   \         P                  ! V\         P                  4      '       d    V #  \         P                  P                  \         P                  P	                  \         P                  P                  \        4      4      R4      # )z!/etc/status-checker/api_keys.jsonSTATUS_CHECKER_KEYSTOREzapi_keys.json)osenvirongetpathdirnameisdiraccessW_OKjoinabspath__file__)	preferredparents     /opt/status-checker/apikeys.py_default_store_pathr      s    3I	zz~~/00zz344WW__Y'F%'WW]]6%:%:ryy!! AF77<<(ABOTT    c                  h    \         P                  ! \        P                  4      P	                  R 4      # )z%Y-%m-%dT%H:%M:%SZ)r   nowr   utcstrftime r   r   _now_isor   %   s!    <<%../CDDr   c                0    V ^8  d   QhR\         R\         /# )   raw_keyreturnstr)formats   "r   __annotate__r$   )   s     ? ?s ?s ?r   c                 h    \         P                  ! V P                  R 4      4      P                  4       # )zutf-8)hashlibsha256encode	hexdigest)r   s   &r   	_hash_keyr*   )   s#    >>'..12<<>>r   c                     \         P                  P                  V 4      '       g   R / /# \        V R4      ;_uu_ 4       p\        P
                  ! V4      uuRRR4       #   + '       g   i     R# ; i)keysrN)r   r
   existsopenjsonload)
store_pathfs   & r   _loadr4   -   sH    77>>*%%|	j#		!yy| 
			s   AA0	c                    \         P                  P                  V4      ;'       g    R p\         P                  ! VRR7       \        P
                  ! VR7      w  r4 \         P                  ! VR4      ;_uu_ 4       p\        P                  ! W^R7       RRR4       \         P                  ! VR4       \         P                  ! WA4       \         P                  P                  V4      '       d    \         P                  ! V4       R# R#   + '       g   i     L}; i  \         d     R# i ; i  \         P                  P                  T4      '       d*    \         P                  ! T4       i   \         d     i i ; ii ; i).T)exist_ok)dirw)indentNi  )r   r
   r   makedirstempfilemkstempfdopenr0   dumpchmodreplacer.   removeOSError)datar2   dir_namefdtmp_pathr3   s   &&    r   _saverH   4   s   wwz*11cHKK4(##1LB
YYr31IIda(  
5!


8(77>>(##		(# $     77>>(##		(#  $sZ   D. ;D	5D. /D 	D	D. D+*D+.&E>E,+E>,E:7E>9E::E>c                0    V ^8  d   QhR\         R\         /# r   labelr    r!   )r#   s   "r   r$   r$   E   s        r   c                8   T;'       g    \         pR\        P                  ! ^ 4      ,           p\        V4      p\        ;_uu_ 4        \        V4      pRV R\        4       RRRRRRR	^ /VR
,          V&   \        WA4       RRR4       V#   + '       g   i     T# ; i)zDCreate a new key, persist its hash, return the RAW key (shown once).sc_rK   createdrevokedF
revoked_atN	last_used	use_countr,   )DEFAULT_STORE_PATHsecretstoken_urlsafer*   _lockr4   r   rH   )rK   r2   r   key_hashrD   s   &&   r   generate_keyrX   E   s    111Jg++B//G!H	Z Uxzu$"
VX 	d 
 N 
 Ns   7BB	c                0    V ^8  d   QhR\         R\        /# rJ   )r"   list)r#   s   "r   r$   r$   X   s      c t r   c                ~   T;'       g    \         p. p\        ;_uu_ 4        \        V4      pVR,          P                  4        FN  pVR,          '       d   K  VR,          V 8X  g   K$  RVR&   \	        4       VR&   VP                  VR,          4       KP  	  \        W14       RRR4       V#   + '       g   i     T# ; i)zERevoke all active keys with this exact label. Returns matched labels.r,   rO   rK   TrP   N)rS   rV   r4   valuesr   appendrH   )rK   r2   matchedrD   metas   &&   r   
revoke_keyr`   X   s    111JG	Z L'')DIG}%"&Y%-Z\"tG}- * 	d 
 N 
 Ns   AB+(9B++B<	c                $    V ^8  d   QhR\         /# r   r    )rZ   )r#   s   "r   r$   r$   i   s      $ r   c                    T ;'       g    \         p \        V 4      pVR,          P                  4        UUu. uF  w  r#RVR,          /VCNK  	  pppVP                  R R7       V# u uppi )uF   Return metadata for all keys (never raw keys — we don't store them).r,   key_id:N   Nc                     V R ,          # )rN   r   )ms   &r   <lambda>list_keys.<locals>.<lambda>n   s    1Y<r   )key)rS   r4   itemssort)r2   rD   hr_   outs   &    r   	list_keysro   i   sg    111JD59&\5G5G5I
J5I'!Haf%%5IC
JHH'H(J Ks   A&c                >    V ^8  d   QhR\         R\         R,          /# )r   r   r    Nr!   )r#   s   "r   r$   r$   r   s      # 3: r   c                   T;'       g    \         pV '       g   R# \        V 4      p\        ;_uu_ 4        \        V4      pVR,          P	                  V4      pV'       d   VR,          '       d    RRR4       R# \        4       VR&   VP	                  R^ 4      ^,           VR&    \        W14       VR,          uuRRR4       #   \         d     L i ; i  + '       g   i     R# ; i)zsReturn the label if the key is valid and active, else None.
Updates last_used / use_count on success (best-effort).Nr,   rO   rQ   rR   rK   )rS   r*   rV   r4   r	   r   rH   rC   )r   r2   rW   rD   r_   s   &&   r   validate_keyrr   r   s     111J!H	Z F|)tI	 

 %J[ HH[!4q8[	$# G} 
  		 
s;   :C/C:)C$C/CCCCCC$	c                $    V ^8  d   QhR\         /# rb   )bool)r#   s   "r   r$   r$      s     @ @4 @r   c                     T ;'       g    \         p \        ;QJ d%    R  \        V 4       4       F  '       g   K   R# 	  R# ! R  \        V 4       4       4      # )c              3   <   "   T F  qR ,          '       * x  K  	  R# 5i)rO   Nr   ).0rg   s   & r   	<genexpr>%has_any_active_key.<locals>.<genexpr>   s     ?)>AY<)>s   TF)rS   anyro   )r2   s   &r   has_any_active_keyr{      sC    111J3?:)>?33?3?3?:)>???r   )N)__doc__r0   r   rT   r&   r<   r   r   	threadingr   r   rS   rV   r   r*   r4   rH   rX   r`   ro   rr   r{   r   r   r   <module>r~      sn     	    ' U )* E?"&"*@ @r   