
    Keh                       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Zddl	Z	ddl
mZmZ ddlmZ ddlmZmZmZ ddlmZmZmZ ddlmZmZmZmZmZ dd	lmZ dd
lm Z  ddl!m"Z"m#Z# ddl$m%Z% erddl&m'Z'm(Z( ddl)m*Z*  ej+        d          Z,e G d d                      Z-ddZ. G d de          Z/ G d de/          Z0 G d de0          Z1dS )zIdentity Provider interface

This defines the _authentication_ layer of Jupyter Server,
to be used in combination with Authorizer for _authorization_.

.. versionadded:: 2.0
    )annotationsN)asdict	dataclass)Morsel)TYPE_CHECKINGAny	Awaitable)escapehttputilweb)BoolDictTypeUnicodedefault)LoggingConfigurable)_i18n   )passwd_checkset_password)get_anonymous_username)AuthenticatedHandlerJupyterHandler)	ServerAppz[^A-Za-z0-9]c                  p    e Zd ZU dZded<   dZded<   dZded<   dZded	<   dZded
<   dZ	ded<   d Z
d ZdS )UserziObject representing a User

    This or a subclass should be returned from IdentityProvider.get_user
    strusername namedisplay_nameN
str | Noneinitials
avatar_urlcolorc                .    |                                   d S N)fill_defaultsselfs    <lib/python3.11/site-packages/jupyter_server/auth/identity.py__post_init__zUser.__post_init__>   s        c                    | j         sd|  }t          |          | j        s| j         | _        | j        s| j        | _        dS dS )zFill out default fields in the identity model

        - Ensures all values are defined
        - Fills out derivative values for name fields fields
        - Fills out null values for optional fields
        z!user.username must not be empty: N)r   
ValueErrorr    r!   )r*   msgs     r+   r(   zUser.fill_defaultsA   sa     } 	"<d<<CS//! y 	&DI  	* $	D	* 	*r-   )__name__
__module____qualname____doc____annotations__r    r!   r#   r$   r%   r,   r(    r-   r+   r   r   &   s          
 MMM DNNNN L  H!J!!!!E
  * * * * *r-   r   got_userr   returnc                d   t          | t                    rt          |           S t          | t                    rci }d| vrd| v r| d         |d<   t          j        D ]}|| v r| |         ||<   	 t          di |S # t
          $ r d|  }t          |          dw xY wd|  }t          |          )a  Backward-compatibility for LoginHandler.get_user

    Prior to 2.0, LoginHandler.get_user could return anything truthy.

    Typically, this was either a simple string username,
    or a simple dict.

    Make some effort to allow common patterns to keep working.
    )r   r   r    zUnrecognized user: Nr6   )
isinstancer   r   dict__dataclass_fields__	TypeErrorr/   )r7   kwargsfieldr0   s       r+   _backward_compat_userr@   U   s     (C   X&&&&	Hd	#	# X%%&H*<*<!)&!1F:. 	0 	0E   (u	,>>&>>! 	, 	, 	,222CS//t+	, /H..oos   /A; ; Bc                     e Zd ZU dZ edd ed                    Zded<    ed ed                    Z	 e
d	dd ed
                    Zded<    ed ed                    Z ed ed                                        d          Zded<    edej        d ed                    Z edej        d ed                    ZdZ ed          d             Z e
d          Zded<   dId ZdJd"ZdKd&ZdLd(ZdMd*ZdNd,ZdOd.ZdPd0Z	 dQdRd6Z dSd7Z!dId8Z" e#j$        d9e#j%                  Z&dTd:Z'dJd;Z(dUd<Z)dVd>Z*dVd?Z+	 dWdXdDZ,dJdEZ-e.dF             Z/e.dG             Z0e.dH             Z1d	S )YIdentityProvideraC  
    Interface for providing identity management and authentication.

    Two principle methods:

    - :meth:`~jupyter_server.auth.IdentityProvider.get_user` returns a :class:`~.User` object
      for successful authentication, or None for no-identity-found.
    - :meth:`~jupyter_server.auth.IdentityProvider.identity_model` turns a :class:`~jupyter_server.auth.User` into a JSONable dict.
      The default is to use :py:meth:`dataclasses.asdict`,
      and usually shouldn't need override.

    Additional methods can customize authentication.

    .. versionadded:: 2.0
    r   TzJName of the cookie to set for persisting login. Default: username-${Host}.confighelpzstr | Unicodecookie_nameziExtra keyword arguments to pass to `set_secure_cookie`. See tornado's set_secure_cookie docs for details.NzSpecify whether login cookie should have the `secure` property (HTTPS-only).Only needed when protocol-detection gives the wrong answer due to proxies.)
allow_nonerD   rE   zbool | Boolsecure_cookieziExtra keyword arguments to pass to `get_secure_cookie`. See tornado's get_secure_cookie docs for details.z<generated>a  Token used for authenticating first-time connections to the server.

        The token can be read from the file referenced by JUPYTER_TOKEN_FILE or set directly
        with the JUPYTER_TOKEN environment variable.

        When no password is enabled,
        the default is to generate a new, random token.

        Setting to an empty string disables authentication altogether, which is NOT RECOMMENDED.

        Prior to 2.0: configured as ServerApp.token
        )rE   )rD   tokenz*jupyter_server.auth.login.LoginFormHandlerz'The login handler class to use, if any.)default_valueklassrD   rE   z(jupyter_server.auth.logout.LogoutHandlerz The logout handler class to use.Fc                   t          j        d          rd| _        t           j        d         S t          j        d          rRd| _        t	          t           j        d                   5 }|                                cd d d            S # 1 swxY w Y   | j        s	d| _        dS d| _        t          j        t          j	        d                    
                    d          S )NJUPYTER_TOKENFJUPYTER_TOKEN_FILEr   T   ascii)osgetenvtoken_generatedenvironopenread
need_tokenbinasciihexlifyurandomdecode)r*   
token_files     r+   _token_defaultzIdentityProvider._token_default   s   9_%% 	/#(D :o..9)** 	)#(D bj!5677 ):!(() ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) 	D#(D 2#'D #BJrNN33::7CCCs   (B		BBrW   handlerr   r8   $User | None | Awaitable[User | None]c                ,    |                      |          S )zGet the authenticated user for a request

        Must return a :class:`jupyter_server.auth.User`,
        though it may be a subclass.

        Return None if the request is not authenticated.

        _may_ be a coroutine
        )	_get_userr*   r^   s     r+   get_userzIdentityProvider.get_user   s     ~~g&&&r-   User | Nonec                  K   t          |dd          r|j        S |                     |          }t          |t                    r| d{V }|}|                     |          }t          |t                    r| d{V }|}|p|}|%|#||k    r|                     ||           d|_        ||                     |          }|	                    |          }|2| j
                            d|            |                     |           | j        s+|                     |          }|                     ||           |S )Get the user._jupyter_current_userNTz&Clearing invalid/expired login cookie )getattrrg   get_user_tokenr:   r	   get_user_cookieset_login_cookie_token_authenticatedget_cookie_name
get_cookielogwarningclear_login_cookieauth_enabledgenerate_anonymous_user)	r*   r^   _token_user
token_user_cookie_usercookie_useruserrF   cookies	            r+   ra   zIdentityProvider._get_user   s     73T:: 	100<@<O<OPW<X<Xk9-- 	, +++++++K"-
++G44lI.. 	.!-------L#/ ([
 6 {""%%gt444 ,0G(< ..w77K''44F!  !W+!W!WXXX''000$ 5 33G<< %%gt444r-   rx   r   r;   c                     t          |          S )z"Return a User as an Identity model)r   )r*   rx   s     r+   identity_modelzIdentityProvider.identity_model  s     d||r-   listc                    g }| j         r|                    d| j        f           | j        r|                    d| j        f           |S )zwReturn list of additional handlers for this identity provider

        For example, an OAuth callback handler.
        z/loginz/logout)login_availableappendlogin_handler_classlogout_availablelogout_handler_class)r*   handlerss     r+   get_handlerszIdentityProvider.get_handlers  sZ    
  	COOY(@ABBB  	EOOZ)BCDDDr-   r   c                l    t          j        |j        |j        |j        |j        |j        d          }|S )zSerialize a user to a string for storage in a cookie

        If overriding in a subclass, make sure to define user_from_cookie as well.

        Default is just the user's username.
        )r   r    r!   r#   r%   )jsondumpsr   r    r!   r#   r%   )r*   rx   ry   s      r+   user_to_cookiezIdentityProvider.user_to_cookie%  s@      M	 $ 1 M 
 
 r-   cookie_valuec           	         t          j        |          }t          |d         |d         |d         |d         d|d                   S )zInverse of user_to_cookier   r    r!   r#   Nr%   )r   loadsr   )r*   r   rx   s      r+   user_from_cookiez!IdentityProvider.user_from_cookie8  sL    z,''L M
 
 	
r-   r   c                n    | j         r| j         S t                              dd|j        j                   S )zReturn the login cookie name

        Uses IdentityProvider.cookie_name, if defined.
        Default is to generate a string taking host into account to avoid
        collisions for multiple servers on one hostname with different ports.
        -z	username-)rF   _non_alphanumsubrequesthostrb   s     r+   rm   z IdentityProvider.get_cookie_nameD  s=      	N## $$S*Lgo6J*L*LMMMr-   Nonec                p   i }|                     | j                   |                    dd           | j        }||j        j        dk    }|r|                    dd           |                    d|j                   |                     |          } |j        || 	                    |          fi | dS )z9Call this on handlers to set the login cookie for successhttponlyTNhttpssecurepath)
updatecookie_options
setdefaultrH   r   protocolbase_urlrm   set_secure_cookier   )r*   r^   rx   r   rH   rF   s         r+   rk   z!IdentityProvider.set_login_cookieP  s    d1222!!*d333 * #O4?M 	6%%h555!!&'*:;;;**733!!+t/B/B4/H/H[[N[[[[[r-   /r    r   domainr"   c                   t          j        |          }t          j                            t          j        j                  t          j        d          z
  }t                      }|                    |dd           t          j
        |          |d<   ||d<   |r||d<   |                    d	|                                           d
S )a  Deletes the cookie with the given name.

        Tornado's cookie handling currently (Jan 2018) stores cookies in a dict
        keyed by name, so it can only modify one cookie with a given name per
        response. The browser can store multiple cookies with the same name
        but different domains and/or paths. This method lets us clear multiple
        cookies with the same name.

        Due to limitations of the cookie protocol, you must pass the same
        path and domain to clear a cookie as were used when that cookie
        was set (but there is no way to find out on the server side
        which values were used for a given cookie).
        )tzim  )daysr   z""expiresr   r   z
Set-CookieN)r
   
native_strdatetimenowtimezoneutc	timedeltar   setr   format_timestamp
add_headerOutputString)r*   r^   r    r   r   r   morsels          r+   _force_clear_cookiez$IdentityProvider._force_clear_cookie`  s       &&#''8+<+@'AAHDV\_D`D`D``

4T"""$5g>>yv 	&%F8<)<)<)>)>?????r-   c                   i }|                     | j                   |                    d|j                  }|                     |          }|                    ||           |r|dk    r|                     ||           dS dS dS )z<Clear the login cookie, effectively logging out the session.r   )r   r   N)r   r   r   r   rm   clear_cookier   )r*   r^   r   r   rF   s        r+   rq   z#IdentityProvider.clear_login_cookie{  s    d1222((1ABB**733[t444 	;DCKK
 $$Wk:::::	; 	;KKr-   c                R    |j         |                     |          fi | j        }|sdS |                                }	 |                     |          S # t
          $ rG}| j                            d| d           | j                            d|            Y d}~dS d}~ww xY w)z[Get user from a cookie

        Calls user_from_cookie to deserialize cookie value
        Nz)Error unpacking user from cookie: cookie=T)exc_infoz"Error unpacking user from cookie: )	get_secure_cookierm   get_secure_cookie_kwargsr[   r   	Exceptionro   debugerror)r*   r^   _user_cookieuser_cookiees        r+   rj   z IdentityProvider.get_user_cookie  s    
 1w0  ))
 
+
 
  	4"))++	((555 	 	 	HNNT{TT_cNdddHNNCCCDDD44444		s    A 
B&<B!!B&z(token|bearer)\s+(.+)c                    |                     dd          }|sO| j                            |j        j                            dd                    }|r|                    d          }|S )zGet the user token from a request

        Default:

        - in URL parameters: ?token=<token>
        - in header: Authorization: token <token>
        rI   r   Authorization   )get_argumentauth_header_patmatchr   headersgetgroup)r*   r^   
user_tokenms       r+   	get_tokenzIdentityProvider.get_token  sj     ))'266
 	($**7?+B+F+FXZ+[+[\\A (WWQZZ
r-   c                F  K   |j         }|sdS |                     |          }d}||k    r'| j                            d|j        j                   d}|rM|                     |          }t          |t                    r| d{V }|}|| 	                    |          }|S dS )zIdentify the user based on a token in the URL or Authorization header

        Returns:
        - uuid if authenticated
        - None if not
        NFz-Accepting token-authenticated request from %sT)
rI   r   ro   r   r   	remote_iprj   r:   r	   rs   )r*   r^   rI   r   authenticated_userrx   s          r+   ri   zIdentityProvider.get_user_token  s        	4^^G,,
HNN?)   !M 	 ((11E%++ $# %D|33G<<K4r-   c                    t          j                    j        }t                      }d| x}}d|d          }d}|j                            d|            t          ||||d|          S )zGenerate a random anonymous user.

        For use when a single shared token is used,
        but does not identify a user.
        z
Anonymous Ar   Nz5Generating new user for token-authenticated request: )uuiduuid4hexr   ro   r   r   )r*   r^   user_idmoonr    r!   r#   r%   s           r+   rs   z(IdentityProvider.generate_anonymous_user  s{     *,,"%''14111| tAw==[RY[[\\\GT<4GGGr-   boolc                .    |                      |           S )a3  Should the Handler check for CORS origin validation?

        Origin check should be skipped for token-authenticated requests.

        Returns:
        - True, if Handler must check for valid CORS origin.
        - False, if Handler should skip origin check since requests are token-authenticated.
        )is_token_authenticatedrb   s     r+   should_check_originz$IdentityProvider.should_check_origin  s     ..w7777r-   c                2    |j          t          |dd          S )zReturns True if handler has been token authenticated. Otherwise, False.

        Login with a token is used to signal certain things, such as:

        - permit access to REST API
        - xsrf protection
        - skip origin-checks for scripts
        rl   F)current_userrh   rb   s     r+   r   z'IdentityProvider.is_token_authenticated  s      	w 6>>>r-   appr   ssl_optionsdict | Nonec                    |j         sId}||j                            | d           | j        s|j                            | d           dS dS | j        s|j                            d           dS dS )z~Check the application's security.

        Show messages, or abort if necessary, based on the security configuration.
        z<WARNING: The Jupyter server is listening on all IP addressesNz3 and not using encryption. This is not recommended.zK and not using authentication. This is highly insecure and not recommended.z`All authentication is disabled.  Anyone who can connect to this server will be able to run code.)ipro   rp   rr   )r*   r   r   rp   s       r+   validate_securityz"IdentityProvider.validate_security  s     v 	TG"7 _ _ _```$  C C C     
 " 	GOOT    	 	r-   c                    |                     dd          }d}| j        s/| j                            d           |                     |          S | j        r | j        |k    r|                     |          S |S )_Process login form data

        Return authenticated User if successful, None if not.
        passwordr   r   N6Accepting anonymous login because auth fully disabled!)r   rr   ro   rp   rs   rI   user_for_token)r*   r^   typed_passwordrx   s       r+   process_login_formz#IdentityProvider.process_login_form  s    
 !--j"-EE  	9HUVVV//888: 	7$*66&&~666r-   c                    dS )zIs authentication enabled?

        Should always be True, but may be False in rare, insecure cases
        where requests with no auth are allowed.

        Previously: LoginHandler.get_login_available
        Tr6   r)   s    r+   rr   zIdentityProvider.auth_enabled!  s	     tr-   c                    | j         S z\Whether a LoginHandler is needed - and therefore whether the login page should be displayed.rr   r)   s    r+   r~   z IdentityProvider.login_available,         r-   c                    dS )z"Whether a LogoutHandler is needed.Tr6   r)   s    r+   r   z!IdentityProvider.logout_available1  s	     tr-   )r^   r   r8   r_   r^   r   r8   rd   )rx   r   r8   r;   )r8   r|   )rx   r   r8   r   )r   r   r8   rd   )r^   r   r8   r   )r^   r   rx   r   r8   r   )r   N)
r^   r   r    r   r   r   r   r"   r8   r   )r^   r   r8   r   )r^   r   r8   r"   )r^   r   r8   r   r^   r   r8   r   r'   r   r   r   r   r8   r   )2r1   r2   r3   r4   r   r   rF   r5   r   r   r   rH   r   tagrI   r   r   RequestHandlerr   r   rS   r   r]   rW   rc   ra   r{   r   r   r   rm   rk   r   rq   rj   recompile
IGNORECASEr   r   ri   rs   r   r   r   r   propertyrr   r~   r   r6   r-   r+   rB   rB   r   s           ")
U_``" " "K     TUA
 
  N "&UY
 
	" " "M      $tUA
 
      #7U
 
    
cc! 
    $ $B U<==	    4@ U566	   OWWD D D  #d4jjJ((((
' 
' 
' 
'+ + + +Z   

 
 
 
   &

 

 

 


N 
N 
N 
N\ \ \ \" _c@ @ @ @ @6; ; ; ;   * !bj!92=IIO    ! ! ! !FH H H H	8 	8 	8 	8? ? ? ?  $(    0       X ! ! X!   X  r-   rB   c                      e Zd ZdZ edd ed                    Z edd ed                    Z edd ed                    Z	 e
d	          d
             Zedd            Zedd            Zd ZddZ	 dd fdZ xZS )PasswordIdentityProviderzA password identity provider.r   Ta  
            Hashed password to use for web authentication.

            To generate, type in a python/IPython shell:

                from jupyter_server.auth import passwd; passwd()

            The string should be of the form type:salt:hashed-password.
            rC   Fao  
            Forces users to use a password for the Jupyter server.
            This is useful in a multi user environment, for instance when
            everybody in the LAN can access each other's machine through ssh.

            In such a case, serving on localhost is not secure since
            any user can connect to the Jupyter server via ssh.

            a  
            Allow password to be changed at login for the Jupyter server.

            While logging in with a token, the Jupyter server UI will give the opportunity to
            the user to enter a new password at the same time that will replace
            the token login mechanism.

            This can be set to False to prevent changing password from the UI/API.
            rW   c                ,    t          | j                   S r'   )r   hashed_passwordr)   s    r+   _need_token_defaultz,PasswordIdentityProvider._need_token_defaultj  s    ,----r-   r8   r   c                    | j         S r   r   r)   s    r+   r~   z(PasswordIdentityProvider.login_availablen  r   r-   c                8    t          | j        p| j                  S )z"Return whether any auth is enabled)r   r   rI   r)   s    r+   rr   z%PasswordIdentityProvider.auth_enableds  s     D(6DJ777r-   c                ,    t          | j        |          S )z1Check password against our stored hashed password)r   r   )r*   r   s     r+   r   z%PasswordIdentityProvider.passwd_checkx  s    D0(;;;r-   r^   r   rd   c                   |                     dd          }|                     dd          }d}| j        s/| j                            d           |                     |          S |                     |          r|s|                     |          S | j        r| j        |k    r|                     |          }|r| j        r{|j        	                    dd          }t          j                            |d          }t          ||	          | _        | j                            t!          d
|                      |S )r   r   r   r   new_passwordNr   
config_dirzjupyter_server_config.json)config_filezWrote hashed password to )r   rr   ro   rp   rs   r   rI   allow_password_changesettingsr   rQ   r   joinr   r   infor   )r*   r^   r   r   rx   r  r  s          r+   r   z+PasswordIdentityProvider.process_login_form|  sM   
 !--j"-EE++NB+GG  	9HUVVV//888^,, 	P\ 	P//888Z 	PDJ.88//88D P : P$-11,CC
 gll:7STT'3Lk'Z'Z'Z$e$M$M$MNNOOOr-   Nr   r   r   r   r   c                   t                                          ||           | j        r| j        s| j                            t          d                     | j                            t          d                     | j                            t          d                     t          j        d           dS dS dS )zHandle security validation.>Jupyter servers are configured to only be run with a password.1Hint: run the following command to set a password)	$ python -m jupyter_server.auth passwordr   N)	superr   password_requiredr   ro   criticalr   sysexit)r*   r   r   	__class__s      r+   r   z*PasswordIdentityProvider.validate_security  s     	!!#{333! 	4+? 	HVWW   He$WXXYYYHe$PQQRRRHQKKKKK	 	 	 	r-   )r8   r   r   r'   r   )r1   r2   r3   r4   r   r   r   r   r  r  r   r   r   r~   rr   r   r   r   __classcell__)r  s   @r+   r   r   7  sl       ''g
U

 

  O  U

 

    !DU

 

    W\. . . ! ! ! X! 8 8 8 X8< < <   6 $(          r-   r   c                      e Zd ZdZ e            Z ed          d             Z ed          d             Ze	d             Z
ddZe	d             ZddZddZ	 dddZdS )LegacyIdentityProviderzLegacy IdentityProvider for use with custom LoginHandlers

    Login configuration has moved from LoginHandler to IdentityProvider
    in Jupyter Server 2.0.
    r  c                     | j         | j        dS )N)rI   r   )rI   r   r)   s    r+   _default_settingsz(LegacyIdentityProvider._default_settings  s     Z,
 
 	
r-   r   c                    ddl m} |S )Nr   )LegacyLoginHandler)loginr  )r*   r  s     r+   _default_login_handler_classz3LegacyIdentityProvider._default_login_handler_class  s    ------!!r-   c                    | j         S r'   )r~   r)   s    r+   rr   z#LegacyIdentityProvider.auth_enabled  s    ##r-   r^   r   r8   rd   c                \    | j                             |          }|dS t          |          S )rf   N)r   rc   r@   )r*   r^   rx   s      r+   rc   zLegacyIdentityProvider.get_user  s0    '0099<4$T***r-   c                @    | j                             | j                  S r'   )r   get_login_availabler  r)   s    r+   r~   z&LegacyIdentityProvider.login_available  s"    ';;M
 
 	
r-   r   r   c                6    | j                             |          S )zWhether we should check origin.)r   r   rb   s     r+   r   z*LegacyIdentityProvider.should_check_origin  s    ';;GDDDr-   c                6    | j                             |          S )z#Whether we are token authenticated.)r   r   rb   s     r+   r   z-LegacyIdentityProvider.is_token_authenticated  s    '>>wGGGr-   Nr   r   r   r   r   c                f   | j         r| j        s| j                            t	          d                     | j                            t	          d                     | j                            t	          d                     t          j        d           | j                            ||          S )zValidate security.r  r	  r
  r   )	r  r   ro   r  r   r  r  r   r   )r*   r   r   s      r+   r   z(LegacyIdentityProvider.validate_security  s     ! 	4+? 	HVWW   He$WXXYYYHe$PQQRRRHQKKK'99
 
 	
r-   r   r   r'   r   )r1   r2   r3   r4   r   r  r   r  r  r   rr   rc   r~   r   r   r   r6   r-   r+   r  r    s         tvvHWZ
 
 
 W"##" " $#"
 $ $ X$+ + + + 
 
 X

E E E EH H H H $(
 
 
 
 
 
 
r-   r  )r7   r   r8   r   )2r4   
__future__r   rX   r   r   rQ   r   r  r   dataclassesr   r   http.cookiesr   typingr   r   r	   tornador
   r   r   	traitletsr   r   r   r   r   traitlets.configr   jupyter_server.transutilsr   securityr   r   utilsr   jupyter_server.base.handlersr   r   jupyter_server.serverappr   r   r   r   r@   rB   r   r  r6   r-   r+   <module>r-     sk    # " " " " "    				 				 



  ) ) ) ) ) ) ) )       0 0 0 0 0 0 0 0 0 0 ) ) ) ) ) ) ) ) ) ) 8 8 8 8 8 8 8 8 8 8 8 8 8 8 0 0 0 0 0 0 + + + + + + 0 0 0 0 0 0 0 0 ) ) ) ) ) )  3QQQQQQQQ222222
?++ +* +* +* +* +* +* +* +*\   :B B B B B* B B BJj j j j j/ j j jZ?
 ?
 ?
 ?
 ?
5 ?
 ?
 ?
 ?
 ?
r-   