diff --git a/jupyter_server/auth/login.py b/jupyter_server/auth/login.py index ca48767f58..fe1e07855a 100644 --- a/jupyter_server/auth/login.py +++ b/jupyter_server/auth/login.py @@ -4,6 +4,8 @@ import os import re import uuid +import warnings +from functools import wraps from urllib.parse import urlparse from tornado.escape import url_escape @@ -12,6 +14,31 @@ from .security import passwd_check, set_password +def _moved_to_identity_provider(new_name_or_method): + """Warn about deprecated LoginHandler methods that have been moved to IdentityProvider""" + + def decorator(method): + name = method.__name__ + + @wraps(method) + def wrapped(*args, **kwargs): + warnings.warn( + f"LoginHandler.{name} is deprecated in jupyter-server 2.0. Use IdentityProvider.{new_name}", + DeprecationWarning, + stacklevel=3, + ) + return method(*args, **kwargs) + + if isinstance(new_name_or_method, str): + # called with an arg + new_name = new_name_or_method + return decorator + else: + method = new_name_or_method + new_name = method.__name__ + return decorator(method) + + class LoginHandler(JupyterHandler): """The basic tornado login handler @@ -67,9 +94,11 @@ def get(self): self._render() @property + # @_moved_to_identity_provider("password") def hashed_password(self): return self.password_from_settings(self.settings) + @_moved_to_identity_provider def passwd_check(self, a, b): return passwd_check(a, b) @@ -96,6 +125,7 @@ def post(self): self._redirect_safe(next_url) @classmethod + @_moved_to_identity_provider def set_login_cookie(cls, handler, user_id=None): """Call this on handlers to set the login cookie for success""" cookie_options = handler.settings.get("cookie_options", {}) @@ -111,6 +141,7 @@ def set_login_cookie(cls, handler, user_id=None): auth_header_pat = re.compile(r"token\s+(.+)", re.IGNORECASE) @classmethod + @_moved_to_identity_provider def get_token(cls, handler): """Get the user token from a request @@ -129,38 +160,24 @@ def get_token(cls, handler): return user_token @classmethod + @_moved_to_identity_provider def should_check_origin(cls, handler): - """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. - """ + """DEPRECATED in 2.0, use IdentityProvider API""" return not cls.is_token_authenticated(handler) @classmethod + @_moved_to_identity_provider def is_token_authenticated(cls, handler): - """Returns 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 - """ + """DEPRECATED in 2.0, use IdentityProvider API""" if getattr(handler, "_user_id", None) is None: # ensure get_user has been called, so we know if we're token-authenticated handler.current_user return getattr(handler, "_token_authenticated", False) @classmethod + @_moved_to_identity_provider def get_user(cls, handler): - """Called by handlers.get_current_user for identifying the current user. - - See tornado.web.RequestHandler.get_current_user for details. - """ + """DEPRECATED in 2.0, use IdentityProvider API""" # Can't call this get_current_user because it will collide when # called on LoginHandler itself. if getattr(handler, "_user_id", None): @@ -196,8 +213,9 @@ def get_user(cls, handler): return user_id @classmethod + @_moved_to_identity_provider def get_user_cookie(cls, handler): - """Get user-id from a cookie""" + """DEPRECATED in 2.0, use IdentityProvider API""" get_secure_cookie_kwargs = handler.settings.get("get_secure_cookie_kwargs", {}) user_id = handler.get_secure_cookie(handler.cookie_name, **get_secure_cookie_kwargs) if user_id: @@ -205,13 +223,9 @@ def get_user_cookie(cls, handler): return user_id @classmethod + @_moved_to_identity_provider def get_user_token(cls, handler): - """Identify the user based on a token in the URL or Authorization header - - Returns: - - uuid if authenticated - - None if not - """ + """DEPRECATED in 2.0, use IdentityProvider API""" token = handler.token if not token: return @@ -242,11 +256,9 @@ def get_user_token(cls, handler): return None @classmethod + @_moved_to_identity_provider def validate_security(cls, app, ssl_options=None): - """Check the application's security. - - Show messages, or abort if necessary, based on the security configuration. - """ + """DEPRECATED in 2.0, use IdentityProvider API""" if not app.ip: warning = "WARNING: The Jupyter server is listening on all IP addresses" if ssl_options is None: @@ -264,14 +276,14 @@ def validate_security(cls, app, ssl_options=None): ) @classmethod + @_moved_to_identity_provider def password_from_settings(cls, settings): - """Return the hashed password from the tornado settings. - - If there is no configured password, an empty string will be returned. - """ + """DEPRECATED in 2.0, use IdentityProvider API""" return settings.get("password", "") @classmethod + @_moved_to_identity_provider def get_login_available(cls, settings): - """Whether this LoginHandler is needed - and therefore whether the login page should be displayed.""" + """DEPRECATED in 2.0, use IdentityProvider API""" + return bool(cls.password_from_settings(settings) or settings.get("token"))