o
    ^it                     @  sD  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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mZ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(dZ)e
j*d Z+eG dd dZ,dddZ-G dd de Z.G dd de.Z/G dd de/Z0d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)escapehttputilweb)	BoolDictEnumList
TraitErrorTypeUnicodedefaultvalidate)LoggingConfigurable)_i18n   )passwd_checkset_password)get_anonymous_usernamez[^A-Za-z0-9]namedisplay_nameinitials
avatar_urlcolorc                   @  sf   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d Z
dd ZdS )UserziObject representing a User

    This or a subclass should be returned from IdentityProvider.get_user
    strusername r   r   N
str | Noner   r   r   c                 C  s   |    d S N)fill_defaultsself r'   W/home/ubuntu/hpml_nyu/venv/lib/python3.10/site-packages/jupyter_server/auth/identity.py__post_init__>   s   zUser.__post_init__c                 C  s<   | 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&   msgr'   r'   r(   r$   A   s   	
zUser.fill_defaults)__name__
__module____qualname____doc____annotations__r   r   r   r   r   r)   r$   r'   r'   r'   r(   r   &   s   
 r   got_usert.Anyreturnc                 C  s   t | tr
t| dS t | trIi }d| vrd| v r| d |d< tjD ]}|| v r.| | ||< q"ztdi |W S  tyH   d|  }t|d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: Nr'   )
isinstancer   r   dict__dataclass_fields__	TypeErrorr*   )r1   kwargsfieldr+   r'   r'   r(   _backward_compat_userU   s$   







r:   c                   @  s"  e Zd ZU dZeddeddZded< ededdZ	e
d	dded
dZded< ededdZededdjddZded< edejdeddZedejdeddZeeeeedgdeddZdZeddd Zedd d! Ze
dZ d"ed#< dpd(d)Z!dqd+d,Z"drd0d1Z#dsd3d4Z$dtd6d7Z%dud8d9Z&dvd<d=Z'dwd?d@Z(dxdBdCZ)dydEdFZ*dzdGdHZ+d{dIdJZ,		d|d}dPdQZ-dudRdSZ.dpdTdUZ/e01dVe0j2Z3d~dWdXZ4dqdYdZZ5dd[d\Z6dd^d_Z7dd`daZ8		dddfdgZ9dqdhdiZ:e;djdk Z<e;dldm Z=e;dndo Z>d	S )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[str, str | bytes]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_noner=   r>   z+bool | Bool[bool | None, bool | int | None]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
        )r>   )r=   tokenz*jupyter_server.auth.login.LoginFormHandlerz'The login handler class to use, if any.)default_valueklassr=   r>   z(jupyter_server.auth.logout.LogoutHandlerz The logout handler class to use.r   z5List of fields in the User model that can be updated.)traitrC   r=   r>   Fc                 C  s   t drd| _t jd S t dr0d| _tt jd }| W  d    S 1 s+w   Y  | js8d| _dS d| _tt 	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_filer'   r'   r(   _token_default   s   


 zIdentityProvider._token_defaultupdatable_fieldsc                   sB   t tt  fdd|d D }|rd| }t||d S )z7Validate that all fields in updatable_fields are valid.c                   s   g | ]}| vr|qS r'   r'   ).0r9   valid_updatable_fieldsr'   r(   
<listcomp>   s    z?IdentityProvider._validate_updatable_fields.<locals>.<listcomp>valuez$Invalid fields in updatable_fields: )listtget_argsUpdatableFieldr   )r&   proposalinvalid_fieldsr+   r'   rY   r(   _validate_updatable_fields   s   

z+IdentityProvider._validate_updatable_fieldsz%bool | Bool[bool, t.Union[bool, int]]rP   handlerweb.RequestHandlerr3   &User | None | t.Awaitable[User | None]c                 C  s
   |  |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&   rd   r'   r'   r(   get_user   s   

zIdentityProvider.get_userUser | Nonec           	        s   t |ddrtt|jS | |}t|tjr|I dH }|}| |}t|tjr0|I dH }|}|p5|}|durK|durK||krH| 	|| d|_
|du ry| |}||}|durk| jd|  | | | jsy| |}| 	|| |S )Get the user._jupyter_current_userNTz&Clearing invalid/expired login cookie )getattrr^   castr   rl   get_user_tokenr4   	Awaitableget_user_cookieset_login_cookie_token_authenticatedget_cookie_name
get_cookielogwarningclear_login_cookieauth_enabledgenerate_anonymous_user)	r&   rd   _token_user
token_user_cookie_usercookie_useruserr?   cookier'   r'   r(   rg      s4   







zIdentityProvider._get_user	user_datadict[UpdatableField, str]r   c                 C  s2   |  | tt|j}| ||}| | |S )z3Update user information and persist the user model.)check_updater^   rn   r   current_userupdate_user_modelpersist_user_model)r&   rd   r   r   updated_userr'   r'   r(   update_user(  s
   

zIdentityProvider.update_userNonec                 C  s,   |D ]}|| j vrd| d}t|qdS )z2Raises if some fields to update are not updatable.zField z is not updatableN)rW   r*   )r&   r   r9   r+   r'   r'   r(   r   2  s   
zIdentityProvider.check_updater   c                 C     t zUpdate user information.NotImplementedError)r&   r   r   r'   r'   r(   r   9     z"IdentityProvider.update_user_modelc                 C  r   )z'Persist the user model (i.e. a cookie).r   rh   r'   r'   r(   r   =  r   z#IdentityProvider.persist_user_modelr   dict[str, t.Any]c                 C  s   t |S )z"Return a User as an Identity model)r   )r&   r   r'   r'   r(   identity_modelA  s   zIdentityProvider.identity_modellist[tuple[str, object]]c                 C  s4   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&   handlersr'   r'   r(   get_handlersF  s   zIdentityProvider.get_handlersr   c                 C  s$   t |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&   r   r   r'   r'   r(   user_to_cookieR  s   	zIdentityProvider.user_to_cookiecookie_valuec                 C  s0   t |}t|d |d |d |d d|d S )zInverse of user_to_cookier    r   r   r   Nr   )r   loadsr   )r&   r   r   r'   r'   r(   user_from_cookiee  s   
z!IdentityProvider.user_from_cookiec                 C  s"   | 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-)r?   _non_alphanumsubrequesthostrh   r'   r'   r(   rt   q  s   z IdentityProvider.get_cookie_namec                 C  s|   i }| | j |dd | j}|du r|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
setdefaultrA   r   protocolbase_urlrt   set_secure_cookier   )r&   rd   r   r   rA   r?   r'   r'   r(   rr   }  s   
z!IdentityProvider.set_login_cookie/r   r   domainr"   c                 C  sr   t |}tjjtjjdtjdd }t }||dd t	
||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&   rd   r   r   r   r   morselr'   r'   r(   _force_clear_cookie  s   
z$IdentityProvider._force_clear_cookiec                 C  sZ   i }| | j |d|j}| |}|j||d |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   rt   clear_cookier   )r&   rd   r   r   r?   r'   r'   r(   rx     s   
z#IdentityProvider.clear_login_cookiec              
   C  s   |j | |fi | j}|sdS | }z| |W S  tyB } z| jjd| dd | jd|  W Y d}~dS d}~w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_cookiert   get_secure_cookie_kwargsrT   r   	Exceptionrv   debugerror)r&   rd   _user_cookieuser_cookieer'   r'   r(   rq     s    z IdentityProvider.get_user_cookiez(token|bearer)\s+(.+)c                 C  s:   | dd}|s| 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>
        rB   r!   Authorization   )get_argumentauth_header_patmatchr   headersgetgroup)r&   rd   
user_tokenmr'   r'   r(   	get_token  s   
zIdentityProvider.get_tokenc                   s   t d|j}|sdS | |}d}||kr"| jd|jj d}|rA| |}t	|t j
r4|I dH }|}|du r?| |}|S dS )zIdentify the user based on a token in the URL or Authorization header

        Returns:
        - uuid if authenticated
        - None if not
        r"   NFz-Accepting token-authenticated request from %sT)r^   rn   rB   r   rv   r   r   	remote_iprq   r4   rp   rz   )r&   rd   rB   r   authenticated_userr   r'   r'   r(   ro     s*   



zIdentityProvider.get_user_tokenc                 C  sT   t  j}t }d|  }}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   rv   r   r   )r&   rd   user_idmoonr   r   r   r   r'   r'   r(   rz     s   
z(IdentityProvider.generate_anonymous_userboolc                 C  s   |  | 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_authenticatedrh   r'   r'   r(   should_check_origin  s   	z$IdentityProvider.should_check_originc                 C  s   |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
        rs   F)r   rm   rh   r'   r'   r(   r     s   
z'IdentityProvider.is_token_authenticatedappr2   ssl_optionsdict[str, t.Any] | Nonec                 C  s^   |j s"d}|du r|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.)iprv   rw   ry   )r&   r   r   rw   r'   r'   r(   validate_security(  s   	z"IdentityProvider.validate_securityc                 C  sT   |j ddd}d}| js| jd | |S | jr(| j|kr(tt| 	|S |S )_Process login form data

        Return authenticated User if successful, None if not.
        passwordr!   r   N6Accepting anonymous login because auth fully disabled!)
r   ry   rv   rw   rz   rB   r^   rn   r   user_for_token)r&   rd   typed_passwordr   r'   r'   r(   process_login_form@  s   
z#IdentityProvider.process_login_formc                 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
        Tr'   r%   r'   r'   r(   ry   P  s   	zIdentityProvider.auth_enabledc                 C     | j S z\Whether a LoginHandler is needed - and therefore whether the login page should be displayed.ry   r%   r'   r'   r(   r   [     z IdentityProvider.login_availablec                 C  r   )z"Whether a LogoutHandler is needed.Tr'   r%   r'   r'   r(   r   `  s   z!IdentityProvider.logout_available)rd   re   r3   rf   rd   re   r3   rj   )rd   re   r   r   r3   r   )r   r   r3   r   r   r   r   r   r3   r   rd   re   r3   r   )r   r   r3   r   )r3   r   )r   r   r3   r   )r   r   r3   rj   )rd   re   r3   r   )rd   re   r   r   r3   r   )r   N)
rd   re   r   r   r   r   r   r"   r3   r   )rd   re   r3   r"   )rd   re   r3   r   rd   re   r3   r   r#   r   r2   r   r   r3   r   )?r,   r-   r.   r/   r   r   r?   r0   r
   r   r	   rA   r   tagrB   r   r   RequestHandlerr   r   r   r   r]   r^   r_   r`   rW   rL   r   rV   r   rc   rP   ri   rg   r   r   r   r   r   r   r   r   rt   rr   r   rx   rq   recompile
IGNORECASEr   r   ro   rz   r   r   r   r   propertyry   r   r   r'   r'   r'   r(   r;   r   s   
 





-














#





r;   c                      s   e Zd ZdZeddeddZeddeddZeddeddZ	e
d	d
d Ze
ddd Zed,ddZed,ddZd-ddZd.ddZd d! Zd/d#d$Z	%d0d1 fd*d+Z  ZS )2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.
            r<   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.
            rP   c                 C  s   t | j S r#   )r   hashed_passwordr%   r'   r'   r(   _need_token_default     z,PasswordIdentityProvider._need_token_defaultrW   c                 C  s   g dS )Nr   r'   r%   r'   r'   r(   _default_updatable_fields  s   z2PasswordIdentityProvider._default_updatable_fieldsr3   r   c                 C  r   r   r   r%   r'   r'   r(   r     r   z(PasswordIdentityProvider.login_availablec                 C  s   t | jp| jS )z"Return whether any auth is enabled)r   r   rB   r%   r'   r'   r(   ry     s   z%PasswordIdentityProvider.auth_enabledr   r   r   r   c                 C  s(   | j D ]}||v rt||||  q|S r   )rW   setattr)r&   r   r   r9   r'   r'   r(   r     s
   
z*PasswordIdentityProvider.update_user_modelrd   re   r   c                 C  s   |  ||j dS )z#Persist the user model to a cookie.N)rr   r   rh   r'   r'   r(   r     s   z+PasswordIdentityProvider.persist_user_modelc                 C  s   t | j|S )z1Check password against our stored hashed password)r   r   )r&   r   r'   r'   r(   r     r   z%PasswordIdentityProvider.passwd_checkrj   c                 C  s   |j ddd}|j 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||d	| _| jtd
j|d |S )r   r   r!   r   new_passwordNr   
config_dirzjupyter_server_config.json)config_filezWrote hashed password to {file})file)r   ry   rv   rw   rz   r   rB   allow_password_changesettingsr   rJ   r   joinr   r   infor   format)r&   rd   r   r  r   r  r  r'   r'   r(   r     s    



z+PasswordIdentityProvider.process_login_formNr   r2   r   r   c                   s`   t  || | jr,| js.| jtd | jtd | jtd t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   rv   criticalr   sysexitr&   r   r   	__class__r'   r(   r     s   z*PasswordIdentityProvider.validate_securityr3   r   r   r   r   r#   r   )r,   r-   r.   r/   r   r   r   r	   r  r  r   r   r   r   r   ry   r   r   r   r   r   __classcell__r'   r'   r  r(   r   f  sJ    

	


r   c                   @  s|   e Zd ZdZe Zeddd Zeddd Ze	dd	 Z
dddZe	d ddZd!ddZd!ddZ	d"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                 C  s   | j | jdS )N)rB   r   )rB   r   r%   r'   r'   r(   _default_settings  s   z(LegacyIdentityProvider._default_settingsr   c                 C  s   ddl m} |S )Nr   )LegacyLoginHandler)loginr  )r&   r  r'   r'   r(   _default_login_handler_class  s   z3LegacyIdentityProvider._default_login_handler_classc                 C  r   r#   )r   r%   r'   r'   r(   ry     s   z#LegacyIdentityProvider.auth_enabledrd   re   r3   rj   c                 C  s    | j |}|du rdS t|S )rk   N)r   ri   r:   )r&   rd   r   r'   r'   r(   ri     s   zLegacyIdentityProvider.get_userr   c                 C  s   t | j| jS r#   )r   r   get_login_availabler  r%   r'   r'   r(   r   
  s
   z&LegacyIdentityProvider.login_availablec                 C     t | j|S )zWhether we should check origin.)r   r   r   rh   r'   r'   r(   r        z*LegacyIdentityProvider.should_check_originc                 C  r  )z#Whether we are token authenticated.)r   r   r   rh   r'   r'   r(   r     r  z-LegacyIdentityProvider.is_token_authenticatedNr   r2   r   r   r   c                 C  sX   | j r#| js#| jtd | jtd | jtd td | j|| dS )zValidate security.r  r  r  r   N)	r  r   rv   r  r   r  r  r   r   r  r'   r'   r(   r     s   
z(LegacyIdentityProvider.validate_securityr   r  r   r#   r   )r,   r-   r.   r/   r
   r  r   r  r  r   ry   ri   r   r   r   r   r'   r'   r'   r(   r    s     





r  )r1   r2   r3   r   )1r/   
__future__r   rQ   r   r   rJ   r   r  typingr^   r   dataclassesr   r   http.cookiesr   tornador   r   r   	traitletsr	   r
   r   r   r   r   r   r   r   traitlets.configr   jupyter_server.transutilsr   securityr   r   utilsr   r   r   Literalr`   r   r:   r;   r   r  r'   r'   r'   r(   <module>   s<    ,


.   w 