diff --git a/jupyterhub/apihandlers/auth.py b/jupyterhub/apihandlers/auth.py index 15a4d783..b7c4df0e 100644 --- a/jupyterhub/apihandlers/auth.py +++ b/jupyterhub/apihandlers/auth.py @@ -201,7 +201,7 @@ class OAuthAuthorizeHandler(OAuthHandler, BaseHandler): def needs_oauth_confirm(self, user, oauth_client): """Return whether the given oauth client needs to prompt for access for the given user - Checks whitelist for oauth clients + Checks list for oauth clients that don't need confirmation (i.e. the user's own server) @@ -214,9 +214,8 @@ class OAuthAuthorizeHandler(OAuthHandler, BaseHandler): if ( # it's the user's own server oauth_client.identifier in own_oauth_client_ids - # or it's in the global whitelist - or oauth_client.identifier - in self.settings.get('oauth_no_confirm_whitelist', set()) + # or it's in the global no-confirm list + or oauth_client.identifier in self.settings.get('oauth_no_confirm', set()) ): return False # default: require confirmation @@ -229,7 +228,7 @@ class OAuthAuthorizeHandler(OAuthHandler, BaseHandler): Render oauth confirmation page: "Server at ... would like permission to ...". - Users accessing their own server or a service whitelist + Users accessing their own server or a blessed service will skip confirmation. """ diff --git a/jupyterhub/app.py b/jupyterhub/app.py index 8b2119ed..ba549aa4 100644 --- a/jupyterhub/app.py +++ b/jupyterhub/app.py @@ -1689,22 +1689,22 @@ class JupyterHub(Application): # the admin_users config variable will never be used after this point. # only the database values will be referenced. - whitelist = [ + allowed = [ self.authenticator.normalize_username(name) - for name in self.authenticator.whitelist + for name in self.authenticator.allowed ] - self.authenticator.whitelist = set(whitelist) # force normalization - for username in whitelist: + self.authenticator.allowed = set(allowed) # force normalization + for username in allowed: if not self.authenticator.validate_username(username): raise ValueError("username %r is not valid" % username) - if not whitelist: + if not allowed: self.log.info( - "Not using whitelist. Any authenticated user will be allowed." + "Not using allowed user list. Any authenticated user will be allowed." ) - # add whitelisted users to the db - for name in whitelist: + # add allowed users to the db + for name in allowed: user = orm.User.find(db, name) if user is None: user = orm.User(name=name) @@ -1714,9 +1714,9 @@ class JupyterHub(Application): db.commit() # Notify authenticator of all users. - # This ensures Auth whitelist is up-to-date with the database. - # This lets whitelist be used to set up initial list, - # but changes to the whitelist can occur in the database, + # This ensures Authenticator.allowed is up-to-date with the database. + # This lets .allowed be used to set up initial list, + # but changes to the allowed list can occur in the database, # and persist across sessions. total_users = 0 for user in db.query(orm.User): @@ -1753,9 +1753,9 @@ class JupyterHub(Application): user.created = user.last_activity or datetime.utcnow() db.commit() - # The whitelist set and the users in the db are now the same. + # The allowed set and the users in the db are now the same. # From this point on, any user changes should be done simultaneously - # to the whitelist set and user db, unless the whitelist is empty (all users allowed). + # to the allowed set and user db, unless the allowed set is empty (all users allowed). TOTAL_USERS.set(total_users) @@ -1770,11 +1770,11 @@ class JupyterHub(Application): for username in usernames: username = self.authenticator.normalize_username(username) if not ( - await maybe_future( - self.authenticator.check_whitelist(username, None) - ) + await maybe_future(self.authenticator.check_allowed(username, None)) ): - raise ValueError("Username %r is not in whitelist" % username) + raise ValueError( + "Username %r is not in Authenticator.allowed" % username + ) user = orm.User.find(db, name=username) if user is None: if not self.authenticator.validate_username(username): @@ -1798,11 +1798,13 @@ class JupyterHub(Application): if kind == 'user': name = self.authenticator.normalize_username(name) if not ( - await maybe_future(self.authenticator.check_whitelist(name, None)) + await maybe_future(self.authenticator.check_allowed(name, None)) ): - raise ValueError("Token name %r is not in whitelist" % name) + raise ValueError( + "Token user name %r is not in Authenticator.allowed" % name + ) if not self.authenticator.validate_username(name): - raise ValueError("Token name %r is not valid" % name) + raise ValueError("Token user name %r is not valid" % name) if kind == 'service': if not any(service["name"] == name for service in self.services): self.log.warning( @@ -2183,14 +2185,14 @@ class JupyterHub(Application): else: version_hash = datetime.now().strftime("%Y%m%d%H%M%S") - oauth_no_confirm_whitelist = set() + oauth_no_confirm_list = set() for service in self._service_map.values(): if service.oauth_no_confirm: self.log.warning( "Allowing service %s to complete OAuth without confirmation on an authorization web page", service.name, ) - oauth_no_confirm_whitelist.add(service.oauth_client_id) + oauth_no_confirm_list.add(service.oauth_client_id) settings = dict( log_function=log_request, @@ -2226,7 +2228,7 @@ class JupyterHub(Application): default_server_name=self._default_server_name, named_server_limit_per_user=self.named_server_limit_per_user, oauth_provider=self.oauth_provider, - oauth_no_confirm_whitelist=oauth_no_confirm_whitelist, + oauth_no_confirm_list=oauth_no_confirm_list, concurrent_spawn_limit=self.concurrent_spawn_limit, spawn_throttle_retry_range=self.spawn_throttle_retry_range, active_server_limit=self.active_server_limit, diff --git a/jupyterhub/auth.py b/jupyterhub/auth.py index 9a43d280..476e7441 100644 --- a/jupyterhub/auth.py +++ b/jupyterhub/auth.py @@ -7,6 +7,7 @@ import re import sys import warnings from concurrent.futures import ThreadPoolExecutor +from functools import partial from shutil import which from subprocess import PIPE from subprocess import Popen @@ -100,41 +101,74 @@ class Authenticator(LoggingConfigurable): """ ).tag(config=True) - whitelist = Set( + whitelist = Set(help="Deprecated, use `Authenticator.allowed`", config=True,) + + allowed = Set( help=""" - Whitelist of usernames that are allowed to log in. + Set of usernames that are allowed to log in. Use this with supported authenticators to restrict which users can log in. This is an - additional whitelist that further restricts users, beyond whatever restrictions the + additional list that further restricts users, beyond whatever restrictions the authenticator has in place. If empty, does not perform any additional restriction. + + .. versionchanged:: 1.2 + `Authenticator.whitelist` renamed to `allowed` """ ).tag(config=True) - blacklist = Set( + blocked = Set( help=""" - Blacklist of usernames that are not allowed to log in. + Set of usernames that are not allowed to log in. Use this with supported authenticators to restrict which users can not log in. This is an - additional blacklist that further restricts users, beyond whatever restrictions the + additional block list that further restricts users, beyond whatever restrictions the authenticator has in place. If empty, does not perform any additional restriction. .. versionadded: 0.9 + + .. versionchanged:: 1.2 + `Authenticator.blacklist` renamed to `blocked` """ ).tag(config=True) - @observe('whitelist') - def _check_whitelist(self, change): + _deprecated_aliases = { + "whitelist": ("allowed", "1.2"), + "blacklist": ("blocked", "1.2"), + } + + @observe(*list(_deprecated_aliases)) + def _deprecated_trait(self, change): + """observer for deprecated traits""" + old_attr = change.name + new_attr, version = self._deprecated_aliases.get(old_attr) + new_value = getattr(self, new_attr) + if new_value != change.new: + # only warn if different + # protects backward-compatible config from warnings + # if they set the same value under both names + self.log.warning( + "{cls}.{old} is deprecated in JupyterHub {version}, use {cls}.{new} instead".format( + cls=self.__class__.__name__, + old=old_attr, + new=new_attr, + version=version, + ) + ) + setattr(self, new_attr, change.new) + + @observe('allowed') + def _check_allowed(self, change): short_names = [name for name in change['new'] if len(name) <= 1] if short_names: sorted_names = sorted(short_names) single = ''.join(sorted_names) string_set_typo = "set('%s')" % single self.log.warning( - "whitelist contains single-character names: %s; did you mean set([%r]) instead of %s?", + "Allowed list contains single-character names: %s; did you mean set([%r]) instead of %s?", sorted_names[:8], single, string_set_typo, @@ -260,6 +294,8 @@ class Authenticator(LoggingConfigurable): def __init__(self, **kwargs): super().__init__(**kwargs) + # TODO: properly handle deprecated signature *and* name + # with correct subclass override priority! for method_name in ( 'check_whitelist', 'check_blacklist', @@ -326,39 +362,45 @@ class Authenticator(LoggingConfigurable): username = self.username_map.get(username, username) return username - def check_whitelist(self, username, authentication=None): - """Check if a username is allowed to authenticate based on whitelist configuration + def check_allowed(self, username, authentication=None): + """Check if a username is allowed to authenticate based on configuration Return True if username is allowed, False otherwise. - No whitelist means any username is allowed. + No allowed set means any username is allowed. - Names are normalized *before* being checked against the whitelist. + Names are normalized *before* being checked against the allowed set. .. versionchanged:: 1.0 Signature updated to accept authentication data and any future changes - """ - if not self.whitelist: - # No whitelist means any name is allowed - return True - return username in self.whitelist - def check_blacklist(self, username, authentication=None): - """Check if a username is blocked to authenticate based on blacklist configuration + .. versionchanged:: 1.2 + Renamed check_whitelist to check_allowed + """ + if not self.allowed: + # No allowed set means any name is allowed + return True + return username in self.allowed + + def check_blocked(self, username, authentication=None): + """Check if a username is blocked to authenticate based on Authenticator.blocked configuration Return True if username is allowed, False otherwise. - No blacklist means any username is allowed. + No block list means any username is allowed. - Names are normalized *before* being checked against the blacklist. + Names are normalized *before* being checked against the block list. .. versionadded: 0.9 .. versionchanged:: 1.0 Signature updated to accept authentication data as second argument + + .. versionchanged:: 1.2 + Renamed check_blacklist to check_blocked """ - if not self.blacklist: - # No blacklist means any name is allowed + if not self.blocked: + # No block list means any name is allowed return True - return username not in self.blacklist + return username not in self.blocked async def get_authenticated_user(self, handler, data): """Authenticate the user who is attempting to log in @@ -367,7 +409,7 @@ class Authenticator(LoggingConfigurable): This calls `authenticate`, which should be overridden in subclasses, normalizes the username if any normalization should be done, - and then validates the name in the whitelist. + and then validates the name in the allowed set. This is the outer API for authenticating a user. Subclasses should not override this method. @@ -375,7 +417,7 @@ class Authenticator(LoggingConfigurable): The various stages can be overridden separately: - `authenticate` turns formdata into a username - `normalize_username` normalizes the username - - `check_whitelist` checks against the user whitelist + - `check_allowed` checks against the user allowed .. versionchanged:: 0.8 return dict instead of username @@ -389,7 +431,7 @@ class Authenticator(LoggingConfigurable): else: authenticated = {'name': authenticated} authenticated.setdefault('auth_state', None) - # Leave the default as None, but reevaluate later post-whitelist + # Leave the default as None, but reevaluate later post-allowed-check authenticated.setdefault('admin', None) # normalize the username @@ -400,20 +442,16 @@ class Authenticator(LoggingConfigurable): self.log.warning("Disallowing invalid username %r.", username) return - blacklist_pass = await maybe_future( - self.check_blacklist(username, authenticated) - ) - whitelist_pass = await maybe_future( - self.check_whitelist(username, authenticated) - ) + blocked_pass = await maybe_future(self.check_blocked(username, authenticated)) + allowed_pass = await maybe_future(self.check_allowed(username, authenticated)) - if blacklist_pass: + if blocked_pass: pass else: - self.log.warning("User %r in blacklist. Stop authentication", username) + self.log.warning("User %r blocked. Stop authentication", username) return - if whitelist_pass: + if allowed_pass: if authenticated['admin'] is None: authenticated['admin'] = await maybe_future( self.is_admin(handler, authenticated) @@ -423,7 +461,7 @@ class Authenticator(LoggingConfigurable): return authenticated else: - self.log.warning("User %r not in whitelist.", username) + self.log.warning("User %r not allowed.", username) return async def refresh_user(self, user, handler=None): @@ -479,7 +517,7 @@ class Authenticator(LoggingConfigurable): It must return the username on successful authentication, and return None on failed authentication. - Checking the whitelist is handled separately by the caller. + Checking allowed/blocked is handled separately by the caller. .. versionchanged:: 0.8 Allow `authenticate` to return a dict containing auth_state. @@ -520,10 +558,10 @@ class Authenticator(LoggingConfigurable): This method may be a coroutine. - By default, this just adds the user to the whitelist. + By default, this just adds the user to the allowed set. Subclasses may do more extensive things, such as adding actual unix users, - but they should call super to ensure the whitelist is updated. + but they should call super to ensure the allowed set is updated. Note that this should be idempotent, since it is called whenever the hub restarts for all users. @@ -533,19 +571,19 @@ class Authenticator(LoggingConfigurable): """ if not self.validate_username(user.name): raise ValueError("Invalid username: %s" % user.name) - if self.whitelist: - self.whitelist.add(user.name) + if self.allowed: + self.allowed.add(user.name) def delete_user(self, user): """Hook called when a user is deleted - Removes the user from the whitelist. - Subclasses should call super to ensure the whitelist is updated. + Removes the user from the allowed set. + Subclasses should call super to ensure the allowed set is updated. Args: user (User): The User wrapper object """ - self.whitelist.discard(user.name) + self.allowed.discard(user.name) auto_login = Bool( False, @@ -610,6 +648,38 @@ class Authenticator(LoggingConfigurable): return [('/login', LoginHandler)] +def _deprecated_method(old_name, new_name, version, self, *args, **kwargs): + """Method wrapper for a deprecated method name""" + + warnings.warn( + ( + "{cls}.{old_name} is deprecated in JupyterHub {version}." + " Please use {cls}.{new_name} instead." + ).format( + cls=self.__class__.__name__, + old_name=old_name, + new_name=new_name, + version=version, + ), + DeprecationWarning, + stacklevel=2, + ) + old_method = getattr(self, new_name) + return old_method(*args, **kwargs) + + +# deprecate white/blacklist method names +for _old_name, _new_name, _version in [ + ("check_whitelist", "check_allowed", "1.2"), + ("check_blacklist", "check_blocked", "1.2"), +]: + setattr( + Authenticator, + _old_name, + partial(_deprecated_method, _old_name, _new_name, _version), + ) + + class LocalAuthenticator(Authenticator): """Base class for Authenticators that work with local Linux/UNIX users @@ -669,37 +739,37 @@ class LocalAuthenticator(Authenticator): """ ).tag(config=True) - group_whitelist = Set( - help=""" - Whitelist all users from this UNIX group. + group_whitelist = Set(help="""DEPRECATED: use allowed_groups""",).tag(config=True) - This makes the username whitelist ineffective. + allowed_groups = Set( + help=""" + Allow login from all users in these UNIX groups. + + If set, allowed username set is ignored. """ ).tag(config=True) - @observe('group_whitelist') - def _group_whitelist_changed(self, change): - """ - Log a warning if both group_whitelist and user whitelist are set. - """ - if self.whitelist: + @observe('allowed_groups') + def _allowed_groups_changed(self, change): + """Log a warning if mutually exclusive user and group allowed sets are specified.""" + if self.allowed: self.log.warning( - "Ignoring username whitelist because group whitelist supplied!" + "Ignoring Authenticator.allowed set because Authenticator.allowed_groups supplied!" ) - def check_whitelist(self, username, authentication=None): - if self.group_whitelist: - return self.check_group_whitelist(username, authentication) + def check_allowed(self, username, authentication=None): + if self.allowed_groups: + return self.check_allowed_groups(username, authentication) else: - return super().check_whitelist(username, authentication) + return super().check_allowed(username, authentication) - def check_group_whitelist(self, username, authentication=None): + def check_allowed_groups(self, username, authentication=None): """ - If group_whitelist is configured, check if authenticating user is part of group. + If allowed_groups is configured, check if authenticating user is part of group. """ - if not self.group_whitelist: + if not self.allowed_groups: return False - for grnam in self.group_whitelist: + for grnam in self.allowed_groups: try: group = self._getgrnam(grnam) except KeyError: @@ -843,7 +913,7 @@ class PAMAuthenticator(LocalAuthenticator): Authoritative list of user groups that determine admin access. Users not in these groups can still be granted admin status through admin_users. - White/blacklisting rules still apply. + allowed/blocked rules still apply. """ ).tag(config=True) @@ -986,6 +1056,16 @@ class PAMAuthenticator(LocalAuthenticator): return super().normalize_username(username) +for _old_name, _new_name, _version in [ + ("check_group_whitelist", "check_group_allowed", "1.2"), +]: + setattr( + LocalAuthenticator, + _old_name, + partial(_deprecated_method, _old_name, _new_name, _version), + ) + + class DummyAuthenticator(Authenticator): """Dummy Authenticator for testing diff --git a/jupyterhub/services/auth.py b/jupyterhub/services/auth.py index 518fd7e8..5ad6d163 100644 --- a/jupyterhub/services/auth.py +++ b/jupyterhub/services/auth.py @@ -860,15 +860,15 @@ class HubAuthenticated(object): if kind == 'service': # it's a service, check hub_services if self.hub_services and name in self.hub_services: - app_log.debug("Allowing whitelisted Hub service %s", name) + app_log.debug("Allowing Hub service %s", name) return model else: app_log.warning("Not allowing Hub service %s", name) raise UserNotAllowed(model) if self.hub_users and name in self.hub_users: - # user in whitelist - app_log.debug("Allowing whitelisted Hub user %s", name) + # user in allowed list + app_log.debug("Allowing Hub user %s", name) return model elif self.hub_groups and set(model['groups']).intersection(self.hub_groups): allowed_groups = set(model['groups']).intersection(self.hub_groups) @@ -877,7 +877,7 @@ class HubAuthenticated(object): name, ','.join(sorted(allowed_groups)), ) - # group in whitelist + # group in allowed list return model else: app_log.warning("Not allowing Hub user %s", name) diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index 7c0bf9c4..cdca3c74 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -435,9 +435,9 @@ class Spawner(LoggingConfigurable): 'LC_ALL', ], help=""" - Whitelist of environment variables for the single-user server to inherit from the JupyterHub process. + List of environment variables for the single-user server to inherit from the JupyterHub process. - This whitelist is used to ensure that sensitive information in the JupyterHub process's environment + This list is used to ensure that sensitive information in the JupyterHub process's environment (such as `CONFIGPROXY_AUTH_TOKEN`) is not passed to the single-user server's process. """, ).tag(config=True) @@ -456,7 +456,7 @@ class Spawner(LoggingConfigurable): Environment variables that end up in the single-user server's process come from 3 sources: - This `environment` configurable - - The JupyterHub process' environment variables that are whitelisted in `env_keep` + - The JupyterHub process' environment variables that are listed in `env_keep` - Variables to establish contact between the single-user notebook and the hub (such as JUPYTERHUB_API_TOKEN) The `environment` configurable should be set by JupyterHub administrators to add diff --git a/jupyterhub/tests/test_app.py b/jupyterhub/tests/test_app.py index bc59176b..55000bc7 100644 --- a/jupyterhub/tests/test_app.py +++ b/jupyterhub/tests/test_app.py @@ -93,7 +93,7 @@ def test_generate_config(): os.remove(cfg_file) assert cfg_file in out assert 'Spawner.cmd' in cfg_text - assert 'Authenticator.whitelist' in cfg_text + assert 'Authenticator.allowed' in cfg_text async def test_init_tokens(request): diff --git a/jupyterhub/tests/test_auth.py b/jupyterhub/tests/test_auth.py index 10ae0b1a..3c9b79ec 100644 --- a/jupyterhub/tests/test_auth.py +++ b/jupyterhub/tests/test_auth.py @@ -6,6 +6,7 @@ from unittest import mock import pytest from requests import HTTPError +from traitlets.config import Config from .mocking import MockPAMAuthenticator from .mocking import MockStructGroup @@ -137,8 +138,8 @@ async def test_pam_auth_admin_groups(): assert authorized['admin'] is False -async def test_pam_auth_whitelist(): - authenticator = MockPAMAuthenticator(whitelist={'wash', 'kaylee'}) +async def test_pam_auth_allowed(): + authenticator = MockPAMAuthenticator(allowed={'wash', 'kaylee'}) authorized = await authenticator.get_authenticated_user( None, {'username': 'kaylee', 'password': 'kaylee'} ) @@ -155,11 +156,11 @@ async def test_pam_auth_whitelist(): assert authorized is None -async def test_pam_auth_group_whitelist(): +async def test_pam_auth_allowed_groups(): def getgrnam(name): return MockStructGroup('grp', ['kaylee']) - authenticator = MockPAMAuthenticator(group_whitelist={'group'}) + authenticator = MockPAMAuthenticator(allowed_groups={'group'}) with mock.patch.object(authenticator, '_getgrnam', getgrnam): authorized = await authenticator.get_authenticated_user( @@ -174,7 +175,7 @@ async def test_pam_auth_group_whitelist(): assert authorized is None -async def test_pam_auth_blacklist(): +async def test_pam_auth_blocked(): # Null case compared to next case authenticator = MockPAMAuthenticator() authorized = await authenticator.get_authenticated_user( @@ -183,50 +184,41 @@ async def test_pam_auth_blacklist(): assert authorized['name'] == 'wash' # Blacklist basics - authenticator = MockPAMAuthenticator(blacklist={'wash'}) + authenticator = MockPAMAuthenticator(blocked={'wash'}) authorized = await authenticator.get_authenticated_user( None, {'username': 'wash', 'password': 'wash'} ) assert authorized is None - # User in both white and blacklists: default deny. Make error someday? - authenticator = MockPAMAuthenticator( - blacklist={'wash'}, whitelist={'wash', 'kaylee'} - ) + # User in both allowed and blocked: default deny. Make error someday? + authenticator = MockPAMAuthenticator(blocked={'wash'}, allowed={'wash', 'kaylee'}) authorized = await authenticator.get_authenticated_user( None, {'username': 'wash', 'password': 'wash'} ) assert authorized is None - # User not in blacklist can log in - authenticator = MockPAMAuthenticator( - blacklist={'wash'}, whitelist={'wash', 'kaylee'} - ) + # User not in blocked set can log in + authenticator = MockPAMAuthenticator(blocked={'wash'}, allowed={'wash', 'kaylee'}) authorized = await authenticator.get_authenticated_user( None, {'username': 'kaylee', 'password': 'kaylee'} ) assert authorized['name'] == 'kaylee' - # User in whitelist, blacklist irrelevent - authenticator = MockPAMAuthenticator( - blacklist={'mal'}, whitelist={'wash', 'kaylee'} - ) + # User in allowed, blocked irrelevent + authenticator = MockPAMAuthenticator(blocked={'mal'}, allowed={'wash', 'kaylee'}) authorized = await authenticator.get_authenticated_user( None, {'username': 'wash', 'password': 'wash'} ) assert authorized['name'] == 'wash' # User in neither list - authenticator = MockPAMAuthenticator( - blacklist={'mal'}, whitelist={'wash', 'kaylee'} - ) + authenticator = MockPAMAuthenticator(blocked={'mal'}, allowed={'wash', 'kaylee'}) authorized = await authenticator.get_authenticated_user( None, {'username': 'simon', 'password': 'simon'} ) assert authorized is None - # blacklist == {} - authenticator = MockPAMAuthenticator(blacklist=set(), whitelist={'wash', 'kaylee'}) + authenticator = MockPAMAuthenticator(blocked=set(), allowed={'wash', 'kaylee'}) authorized = await authenticator.get_authenticated_user( None, {'username': 'kaylee', 'password': 'kaylee'} ) @@ -253,7 +245,7 @@ async def test_deprecated_signatures(): async def test_pam_auth_no_such_group(): - authenticator = MockPAMAuthenticator(group_whitelist={'nosuchcrazygroup'}) + authenticator = MockPAMAuthenticator(allowed_groups={'nosuchcrazygroup'}) authorized = await authenticator.get_authenticated_user( None, {'username': 'kaylee', 'password': 'kaylee'} ) @@ -262,7 +254,7 @@ async def test_pam_auth_no_such_group(): async def test_wont_add_system_user(): user = orm.User(name='lioness4321') - authenticator = auth.PAMAuthenticator(whitelist={'mal'}) + authenticator = auth.PAMAuthenticator(allowed={'mal'}) authenticator.create_system_users = False with pytest.raises(KeyError): await authenticator.add_user(user) @@ -270,7 +262,7 @@ async def test_wont_add_system_user(): async def test_cant_add_system_user(): user = orm.User(name='lioness4321') - authenticator = auth.PAMAuthenticator(whitelist={'mal'}) + authenticator = auth.PAMAuthenticator(allowed={'mal'}) authenticator.add_user_cmd = ['jupyterhub-fake-command'] authenticator.create_system_users = True @@ -296,7 +288,7 @@ async def test_cant_add_system_user(): async def test_add_system_user(): user = orm.User(name='lioness4321') - authenticator = auth.PAMAuthenticator(whitelist={'mal'}) + authenticator = auth.PAMAuthenticator(allowed={'mal'}) authenticator.create_system_users = True authenticator.add_user_cmd = ['echo', '/home/USERNAME'] @@ -317,13 +309,13 @@ async def test_add_system_user(): async def test_delete_user(): user = orm.User(name='zoe') - a = MockPAMAuthenticator(whitelist={'mal'}) + a = MockPAMAuthenticator(allowed={'mal'}) - assert 'zoe' not in a.whitelist + assert 'zoe' not in a.allowed await a.add_user(user) - assert 'zoe' in a.whitelist + assert 'zoe' in a.allowed a.delete_user(user) - assert 'zoe' not in a.whitelist + assert 'zoe' not in a.allowed def test_urls(): @@ -461,3 +453,10 @@ async def test_post_auth_hook(): ) assert authorized['testkey'] == 'testvalue' + + +async def test_deprecations(): + cfg = Config() + cfg.Authenticator.whitelist = {'user'} + authenticator = auth.Authenticator(config=cfg) + assert authenticator.allowed == {'user'} diff --git a/jupyterhub/tests/test_pages.py b/jupyterhub/tests/test_pages.py index a1825d43..44e67332 100644 --- a/jupyterhub/tests/test_pages.py +++ b/jupyterhub/tests/test_pages.py @@ -686,7 +686,7 @@ async def test_shutdown_on_logout(app, shutdown_on_logout): assert spawner.ready == (not shutdown_on_logout) -async def test_login_no_whitelist_adds_user(app): +async def test_login_no_allowed_adds_user(app): auth = app.authenticator mock_add_user = mock.Mock() with mock.patch.object(auth, 'add_user', mock_add_user): diff --git a/jupyterhub/tests/test_services_auth.py b/jupyterhub/tests/test_services_auth.py index de4d73e9..752091d2 100644 --- a/jupyterhub/tests/test_services_auth.py +++ b/jupyterhub/tests/test_services_auth.py @@ -185,7 +185,7 @@ def test_hub_authenticated(request): m.get(good_url, text=json.dumps(mock_model)) - # no whitelist + # no specific allowed user r = requests.get( 'http://127.0.0.1:%i' % port, cookies={'jubal': 'early'}, @@ -194,7 +194,7 @@ def test_hub_authenticated(request): r.raise_for_status() assert r.status_code == 200 - # pass whitelist + # pass allowed user TestHandler.hub_users = {'jubalearly'} r = requests.get( 'http://127.0.0.1:%i' % port, @@ -204,7 +204,7 @@ def test_hub_authenticated(request): r.raise_for_status() assert r.status_code == 200 - # no pass whitelist + # no pass allowed ser TestHandler.hub_users = {'kaylee'} r = requests.get( 'http://127.0.0.1:%i' % port, @@ -213,7 +213,7 @@ def test_hub_authenticated(request): ) assert r.status_code == 403 - # pass group whitelist + # pass allowed group TestHandler.hub_groups = {'lions'} r = requests.get( 'http://127.0.0.1:%i' % port, @@ -223,7 +223,7 @@ def test_hub_authenticated(request): r.raise_for_status() assert r.status_code == 200 - # no pass group whitelist + # no pass allowed group TestHandler.hub_groups = {'tigers'} r = requests.get( 'http://127.0.0.1:%i' % port,