+
    jHàjä.  ã                  ó®  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
t
^ RIt^ RIHt ^ RIHtHtHtHt ^ RIHtHtHtHtHt ]P2                  ! ]4      tRtRtRtRt^t ^t!^ t"^t#]PH                  ! 4       PJ                  t&Rs'R](R	&   R
 R lt)R R lt*])! R4      t+R R lt,R R lt- ! R R]]4      t.R R lt/R R lt0R R lt1R R lt2R# ) u^  BasicAuthProvider â€” username/password dashboard auth (no OAuth IDP).

Login is a credential form (``supports_password`` + ``complete_password_login``); cookies,
verify, refresh, ws-tickets and logout are the shared framework. Sessions are stateless
HMAC-signed tokens (no IDP, no database); passwords use stdlib scrypt and login always hashes
even for an unknown username (no username-enumeration timing oracle). Config: ``dashboard.
basic_auth.{username,password_hash|password,secret,session_ttl_seconds}`` or the
``HERMES_DASHBOARD_BASIC_AUTH_*`` env vars (env wins when non-empty; see ``_settings``).
)ÚannotationsN)ÚOptional)ÚDashboardAuthProviderÚInvalidCredentialsErrorÚRefreshExpiredErrorÚSession)ÚNonInteractiveMixinÚSkipRegistrationÚload_config_sectionÚregister_providerÚresolve_env_or_cfgzdashboard-auth-basicÚ ÚstrÚLAST_SKIP_REASONc               ó    € V ^8„  d   QhRRRR/# )é   Úpasswordr   Úreturn© )Úformats   "ÚJ/home/ubuntu/.hermes/hermes-agent/plugins/dashboard_auth/basic/__init__.pyÚ__annotate__r   3   s   € ÷ Mñ M˜Cð M Cñ Mó    c           
     óv  € \         P                  ! \        4      p\        P                  ! V P                  R4      V\        \        \        \        ^ R7      p\        P                  ! V4      P                  4       \        P                  ! V4      P                  4       rCR\         R\         R\         RV RV 2
# )a  Return a ``scrypt$n$r$p$<salt_b64>$<dk_b64>`` hash string. Public so operators can
precompute ``password_hash`` for config.yaml (the plaintext then never sits at rest):
``python -c "from plugins.dashboard_auth.basic import hash_password; print(hash_password('pw'))"``.úutf-8©ÚsaltÚnÚrÚpÚdklenÚmaxmemzscrypt$Ú$)ÚsecretsÚtoken_bytesÚ_SCRYPT_SALT_BYTESÚhashlibÚscryptÚencodeÚ	_SCRYPT_NÚ	_SCRYPT_RÚ	_SCRYPT_PÚ_SCRYPT_DKLENÚbase64Ú	b64encodeÚdecode)r   r   ÚdkÚsalt_b64Údk_b64s   &    r   Úhash_passwordr3   3   s’   € ô ×ÒÔ1Ó2€DÜ	�ŠØ�‰˜Ó  t¬y¼IÌÔZgÐpqô
s€Bä×'Ò'¨Ó-×4Ñ4Ó6¼×8HÒ8HÈÓ8L×8SÑ8SÓ8UˆfØ”Y�K˜q¤ ¨1¬Y¨K°q¸¸
À!ÀFÀ8ÐLÐLr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   Úencodedr   Úboolr   )r   s   "r   r   r   >   s!   € ÷ 1ñ 1˜sð 1¨Sð 1°Tñ 1r   c           
     óÌ  €  VP                  R4      w  r#rErgVR8w  d   R# \        V4      \        V4      \        V4      r©p\        P                  ! V4      p\        P                  ! V4      p \        P                  ! T P                  R4      Y¸Yš\        T4      ^ R7      p\        P                  ! YÜ4      #   \        \
        3 d     R# i ; i  \        \        3 d     R# i ; i)z@Constant-time scrypt verify. False on any malformed hash string.r"   r'   Fr   r   )ÚsplitÚintr-   Ú	b64decodeÚ
ValueErrorÚ	TypeErrorr&   r'   r(   ÚlenÚMemoryErrorÚhmacÚcompare_digest)r   r5   ÚschemeÚn_sÚr_sÚp_sr1   r2   r   r   r   r   ÚexpectedÚactuals   &&            r   Ú_verify_passwordrG   >   sÎ   € ðØ29·-±-ÀÓ2DÑ/ˆ�S˜xØ�XÔÙÜ�c“(œC ›H¤c¨#£hˆaˆÜ×Ò Ó)ˆÜ×#Ò# FÓ+ˆðÜ—’ §¡°Ó 8¸tÈAÔZ]Ð^fÓZgÐpqÔrˆô ×Ò˜vÓ0Ð0øô œ	Ð"ô Úðûô œÐ$ô Úðús)   ‚B6  AB6 Á-3C Â6CÃ
CÃC#Ã"C#z'dummy-password-for-constant-time-verifyc               ó$   € V ^8„  d   QhRRRRRR/# )r   ÚpayloadÚdictÚsecretÚbytesr   r   r   )r   s   "r   r   r   W   s!   € ÷ 8ñ 8�4ð 8 ð 8¨3ñ 8r   c                ó  € \         P                  ! V RR7      P                  4       p\        P                  ! W\
        P                  4      P                  4       p\        P                  ! W#,           4      P                  4       # )Ú,)Ú
separators)rN   Ú:)ÚjsonÚdumpsr(   r?   Únewr&   Úsha256Údigestr-   Úurlsafe_b64encoder/   )rI   rK   ÚrawÚsigs   &&  r   Ú_signrY   W   sT   € Ü
�*Š*�W¨Ô
4×
;Ñ
;Ó
=€CÜ
�(Š(�6¤§¡Ó
/×
6Ñ
6Ó
8€CÜ×#Ò# C¥IÓ.×5Ñ5Ó7Ð7r   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   Útokenr   rK   rL   Úkindr   zOptional[dict]r   )r   s   "r   r   r   ]   s(   € ÷ ñ �3ð  ð ¨Sð °^ñ r   c                ó*  €  \         P                  ! V P                  4       4      p\        V4      \        8:  d   R# VR\        )  V\        ) R rT\
        P                  ! W\        P                  4      P                  4       p\
        P                  ! WV4      '       g   R# \        P                  ! V4      pTP                  R4      T8w  g4   TP                  R^ 4      \        \         P                   ! 4       4      8:  d   R# T#   \         d     R# i ; i)z‡Return the payload if the signature is valid, ``kind`` matches and it
is unexpired; ``None`` otherwise (including on any decode error).Nr\   Úexp)r-   Úurlsafe_b64decoder(   r=   Ú_SIG_LENr?   rS   r&   rT   rU   r@   rQ   ÚloadsÚ	ExceptionÚgetr9   Útime)r[   rK   r\   ÚblobrW   rX   rE   rI   s   &&&     r   Ú_unsignrf   ]   sÏ   € ð
Ü×'Ò'¨¯©«Ó7ˆÜˆt‹9œÔ ÙØ˜
œ(˜Ð# T¬8¨)¨*Ð%5ˆSÜ—8’8˜F¬¯©Ó8×?Ñ?ÓAˆÜ×"Ò" 3×1Ò1ÙÜ—*’*˜S“/ˆð ‡{�{�6Ó˜dÔ" g§k¡k°%¸Ó&;¼sÄ4Ç9Â9Ã;Ó?OÔ&OÙØ€Nøô	 ô Úðús   ‚8D ¼A"D Â D ÄDÄDc                  óŠ   € ] tR t^rtRtRtRtRtRtRt	R]
/R R	 lltR
 R ltR R ltR R ltR R ltR R ltR R ltRtR# )ÚBasicAuthProviderz?Username/password provider with stateless HMAC-signed sessions.ÚbasiczUsername & PasswordTz@BasicAuthProvider is password-only; use complete_password_login.zzBasicAuthProvider is password-only; there is no OAuth redirect flow. The login page POSTs to /auth/password-login instead.Úttl_secondsc          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r   Úusernamer   Úpassword_hashrK   rL   rj   r9   r   ÚNoner   )r   s   "r   r   ÚBasicAuthProvider.__annotate__}   s1   € ÷ 
.ñ 
. Cð 
.¸ð 
.ÀUð 
.ÐY\ð 
.Ðx|ñ 
.r   c               	óà   € V'       g   \        R 4      hV'       g   \        R4      h\        V4      ^8  d   \        R4      hWn        W n        W0n        \        ^<\        V4      4      V n        R# )zusername must be non-emptyzpassword_hash must be non-emptyz secret must be at least 16 bytesN)r;   r=   Ú	_usernameÚ_password_hashÚ_secretÚmaxr9   Ú_ttl)Úselfrl   rm   rK   rj   s   &$$$$r   Ú__init__ÚBasicAuthProvider.__init__}   s[   € ßÜÐ9Ó:Ð:ßÜÐ>Ó?Ð?Üˆv‹;˜ÔÜÐ?Ó@Ð@Ø!ŒØ+ÔØŒÜ˜œC Ó,Ó-ˆŽ	r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rl   r   r   r   r   r   )r   s   "r   r   ro   ‹   s!   € ÷ 2ñ 2°3ð 2À#ð 2È'ñ 2r   c               	ó2  € \         P                  ! VP                  R 4      V P                  P                  R 4      4      p\	        Y#'       d   V P
                  M\        4      pV'       d	   V'       g   \        R4      hV P                  V P                  4      # )r   zinvalid username or password)	r?   r@   r(   rq   rG   rr   Ú_DUMMY_HASHr   Ú_mint_session)rv   rl   r   Úusername_okÚpassword_oks   &$$  r   Úcomplete_password_loginÚ)BasicAuthProvider.complete_password_login‹   sl   € ô ×)Ò)¨(¯/©/¸'Ó*BÀDÇNÁN×DYÑDYÐZaÓDbÓcˆÜ& xÎ°×1DÒ1DÔYdÓeˆß§Ü)Ð*HÓIÐIØ×!Ñ! $§.¡.Ó1Ð1r   c               ó    € V ^8„  d   QhRRRR/# )r   Úaccess_tokenr   r   zOptional[Session]r   )r   s   "r   r   ro   —   s   € ÷ añ a¨cð aÐ6Gñ ar   c               	ó´   € \        WP                  R 4      pVf   R# V P                  \        VP	                  RR4      4      \        VR,          4      VR4      # )ÚaccessNÚsubr   r^   )rf   rs   Ú_sessionr   rc   r9   )rv   r‚   rI   s   &$ r   Úverify_sessionÚ BasicAuthProvider.verify_session—   sI   € Ü˜,¯©°hÓ?ˆØŠ?ÙØ�}‰}œS §¡¨U°BÓ!7Ó8¼#¸gÀe½nÓ:MÈ|Ð]_Ó`Ð`r   c               ó    € V ^8„  d   QhRRRR/# )r   Úrefresh_tokenr   r   r   r   )r   s   "r   r   ro   �   s   € ÷ Kñ K°ð K¸ñ Kr   c               	óÚ   € V'       g   \        R 4      h\        WP                  R4      pVf   \        R4      hV P                  \	        VP                  RV P                  4      4      4      # )z#no refresh token present in sessionÚrefreshz refresh token expired or invalidr…   )r   rf   rs   r|   r   rc   rq   )rv   rŠ   rI   s   &$ r   Úrefresh_sessionÚ!BasicAuthProvider.refresh_session�   sW   € ßÜ%Ð&KÓLÐLÜ˜-¯©°yÓAˆØŠ?Ü%Ð&HÓIÐIØ×!Ñ!¤# g§k¡k°%¸¿¹Ó&HÓ"IÓJÐJr   c               ó    € V ^8„  d   QhRRRR/# )r   rŠ   r   r   rn   r   )r   s   "r   r   ro   ¥   s   € ÷ ñ ¨sð °tñ r   c               	ó   € R # )Nr   )rv   rŠ   s   &$r   Úrevoke_sessionÚ BasicAuthProvider.revoke_session¥   s   € ár   c               ó    € V ^8„  d   QhRRRR/# )r   Úuser_idr   r   r   r   )r   s   "r   r   ro   «   s   € ÷ iñ i Sð i¨Wñ ir   c                	ó  € \        \        P                  ! 4       4      pW P                  ,           pV P                  W\	        R VRRRV/V P
                  4      \	        R VRRRV\        ,           /V P
                  4      4      # )r…   r\   r„   r^   rŒ   )r9   rd   ru   r†   rY   rs   Ú_REFRESH_TTL_SECONDS)rv   r”   Únowr^   s   &&  r   r|   ÚBasicAuthProvider._mint_session«   sr   € Ü”$—)’)“+ÓˆØ—I‘I�oˆØ�}‰}ØÜ�5˜' 6¨8°U¸CÐ@À$Ç,Á,ÓOÜ�5˜' 6¨9°e¸SÔCWÕ=WÐXÐZ^×ZfÑZfÓgóið 	ir   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r   r”   r   r^   r9   r‚   rŠ   r   r   r   )r   s   "r   r   ro   ³   s8   € ÷ Tñ T ð T¨#ð T¸Sð TÐQTð TÐY`ñ Tr   c                	ó:   € \        VR VR V P                  W#VR7      # )r   )r”   ÚemailÚdisplay_nameÚorg_idÚproviderÚ
expires_atr‚   rŠ   )r   Úname)rv   r”   r^   r‚   rŠ   s   &&&&&r   r†   ÚBasicAuthProvider._session³   s(   € ÜØ 2°GÀBÐQU×QZÑQZØÀ]ôTð 	Tr   )rr   rs   ru   rq   N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r    rœ   Úsupports_passwordÚ_NOT_INTERACTIVEÚ_NO_START_LOGINÚ_DEFAULT_TTL_SECONDSrw   r   r‡   r�   r‘   r|   r†   Ú__static_attributes__r   r   r   rh   rh   r   s[   † ÙIà€DØ(€LØÐØYÐð	@ð ð
.Ð_s÷ 
.õ2õaõKõõi÷Tñ Tr   rh   c               ó   € V ^8„  d   QhRR/# ©r   r   rJ   r   )r   s   "r   r   r   »   s   € ÷ Hñ H¨ñ Hr   c                 ó.   € \        \        \        R R4      # )Ú	dashboardÚ
basic_auth)r
   ÚloggerÚ_TAGr   r   r   Ú_load_config_basic_auth_sectionr³   »   s   € Üœv¤t¨[¸,ÓGÐGr   c               ó    € V ^8„  d   QhRRRR/# )r   Úcfg_sectionrJ   r   rL   r   )r   s   "r   r   r   ¿   s   € ÷ ñ  ð ¨%ñ r   c                óx  € \        RV P                  R4      4      pV'       g,   \        P                  R4       \        P
                  ! ^ 4      # \        P                  \        P                  3 F   p V! V4      p\        V4      ^8¼  d   Vu # K"  	  VP                  R4      #   \        \        3 d     KI  i ; i)uÃ   Resolve the token-signing secret (base64, hex, or raw text). When unset, generates
a random per-process secret (sessions then don't survive a restart or span multiple
workers â€” logged at INFO).Ú"HERMES_DASHBOARD_BASIC_AUTH_SECRETrK   zõdashboard-auth-basic: no 'secret' configured; generating a random per-process signing key. Sessions will not survive a restart or span multiple workers. Set dashboard.basic_auth.secret (or HERMES_DASHBOARD_BASIC_AUTH_SECRET) for stable sessions.r   )r   rc   r±   Úinfor#   r$   r-   r:   rL   Úfromhexr=   r;   r<   r(   )rµ   rW   ÚdecoderÚdecodeds   &   r   Ú_resolve_secretr¼   ¿   s«   € ô ÐAÀ;Ç?Á?ÐS[ÓC\Ó
]€CßÜ�‰ðGô	Hô
 ×"Ò" 2Ó&Ð&Ü×$Ñ$¤e§m¡mÓ4ˆð	Ù˜c“lˆGÜ�7‹|˜rÔ!Ø’ñ "ñ 5ð �:‰:�gÓÐøô œIÐ&ô 	Úð	ús   Á3B$Â$B9Â8B9c               ó   € V ^8„  d   QhRR/# r­   r   )r   s   "r   r   r   Õ   s   € ÷ (zñ (z�4ñ (zr   c                 óX  a€ \        4       oR V3R llp V ! RR4      pV ! RR4      pV ! RR4      pV ! R	R
4      pV'       g   \        R4      hV'       g   V'       g   \        RRR7      h\        P                  P	                  RR4      P                  4       pV'       d"   \        V4      p\        P                  R4       M(V'       g!   \        V4      p\        P                  R4        V'       d   \        V4      M\        pRVRVR\        S4      RV/#   \         d
    \        p L&i ; i)zNResolve BasicAuthProvider kwargs from env/config; raises ``SkipRegistration``.c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úenv_namer   Úcfg_keyr   r   )r   s   "r   r   Ú_settings.<locals>.__annotate__Ù   s&   € ÷ Fñ F˜#ð F¨ð F°ñ Fr   c                ó<   <€ \        V SP                  VR 4      4      # )r   )r   rc   )rÀ   rÁ   Úsections   &&€r   ÚsettingÚ_settings.<locals>.settingÙ   s   ø€ Ü! (¨G¯K©K¸ÀÓ,DÓEÐEr   Ú$HERMES_DASHBOARD_BASIC_AUTH_USERNAMErl   Ú)HERMES_DASHBOARD_BASIC_AUTH_PASSWORD_HASHrm   Ú$HERMES_DASHBOARD_BASIC_AUTH_PASSWORDr   Ú'HERMES_DASHBOARD_BASIC_AUTH_TTL_SECONDSÚsession_ttl_secondsa*  dashboard.basic_auth.username is not set (and HERMES_DASHBOARD_BASIC_AUTH_USERNAME is empty). Set a username and a password (or password_hash) under dashboard.basic_auth in config.yaml to enable username/password dashboard login, or use the OAuth provider, or pass --insecure to skip the auth gate.uË   dashboard.basic_auth.username is set but neither password_hash nor password is configured. Provide one of them (password_hash is preferred â€” compute it with plugins.dashboard_auth.basic.hash_password).Úwarning)Úlevelr   zbdashboard-auth-basic: hashed env-supplied password in-memory (overrides any config password_hash).z§dashboard-auth-basic: hashed plaintext password in-memory. For production, precompute dashboard.basic_auth.password_hash and remove the plaintext password from config.rK   rj   )r³   r	   ÚosÚenvironrc   Ústripr3   r±   r¸   r9   rª   r;   r¼   )rÅ   rl   rm   Ú	plaintextÚttl_rawÚplaintext_from_envÚttlrÄ   s          @r   Ú	_settingsrÕ   Õ   s*  ø€ ä-Ó/€G÷Fð Fñ Ð=¸zÓJ€HÙÐGÈÓY€MÙÐ>À
ÓK€IÙÐ?ÐAVÓW€GßÜðZó[ð 	[÷
 §Üð@ð ô	ð 	ô Ÿ™Ÿ™Ð(NÐPRÓS×YÑYÓ[ÐßÜ%Ð&8Ó9ˆÜ�‰ÐxÕyßÜ% iÓ0ˆÜ�‰ð=ô	>ð#ß%Œc�'ŒlÔ+?ˆð ˜ /°=À(ÌOÐ\cÓLdÐfsÐuxÐyÐyøô ô #Ü"Šð#ús   Ã)D Ã1D ÄD)Ä(D)c               ó   € V ^8„  d   QhRR/# )r   r   rn   r   )r   s   "r   r   r      s   € ÷ lñ l�Tñ lr   c                ó”   € Rs \        V \        \        \        \
        4      w  ps Ve    \        P                  RVR,          4       R# R# )z…Register ``BasicAuthProvider`` when username + (password or
password_hash) are configured; a no-op for OAuth / ``--insecure`` setups.r   Nz@dashboard-auth-basic: registered password provider (username=%s)rl   )r   r   r±   r²   rh   rÕ   r¸   )ÚctxÚkwargss   & r   ÚregisterrÚ      sE   € ð ÐÜ0°´f¼dÔDUÔW`ÓaÑ€FÐØÒÜ�‰ÐVÐX^Ð_iÕXjÖkñ r   iÀ¨  i �' i @  )3Ú__conditional_annotations__r¦   Ú
__future__r   r-   r&   r?   rQ   ÚloggingrÎ   r#   rd   Útypingr   Úhermes_cli.dashboard_authr   r   r   r   Úplugins.dashboard_auth._sharedr   r	   r
   r   r   Ú	getLoggerr¢   r±   r²   rª   r–   r)   r*   r+   r,   r%   rT   Údigest_sizer`   r   Ú__annotations__r3   rG   r{   rY   rf   rh   r³   r¼   rÕ   rÚ   )rÛ   s   @r   Ú<module>rä      sì   øðôõ #ã Û Û Û Û Û 	Û Û Ý ç rÓ r÷gõ gð 
×	Ò	˜8Ó	$€Ø€ð $Ð Ø(Ð ð €	Ø€	Ø€	Ø€ØÐ ð �>Š>Ó×'Ñ'€àÐ �#Ó õ
Mõ1ñ( ÐEÓF€õ
8õô*DTÐ+Ð-Bô DTõRHõõ,(z÷Vlr   