Merge pull request #3090 from minrk/words-matter

This commit is contained in:
Min RK
2020-07-03 12:27:08 +02:00
committed by GitHub
15 changed files with 345 additions and 168 deletions

View File

@@ -7,6 +7,8 @@ command line for details.
## [Unreleased] ## [Unreleased]
## 1.1 ## 1.1
### [1.1.0] 2020-01-17 ### [1.1.0] 2020-01-17
@@ -116,7 +118,7 @@ Thanks to everyone who has contributed to this release!
- Log JupyterHub version on startup [#2752](https://github.com/jupyterhub/jupyterhub/pull/2752) ([@consideRatio](https://github.com/consideRatio)) - Log JupyterHub version on startup [#2752](https://github.com/jupyterhub/jupyterhub/pull/2752) ([@consideRatio](https://github.com/consideRatio))
- Reduce verbosity for "Failing suspected API request to not-running server" (new) [#2751](https://github.com/jupyterhub/jupyterhub/pull/2751) ([@rkdarst](https://github.com/rkdarst)) - Reduce verbosity for "Failing suspected API request to not-running server" (new) [#2751](https://github.com/jupyterhub/jupyterhub/pull/2751) ([@rkdarst](https://github.com/rkdarst))
- Add missing package for json schema doc build [#2744](https://github.com/jupyterhub/jupyterhub/pull/2744) ([@willingc](https://github.com/willingc)) - Add missing package for json schema doc build [#2744](https://github.com/jupyterhub/jupyterhub/pull/2744) ([@willingc](https://github.com/willingc))
- blacklist urllib3 versions with encoding bug [#2743](https://github.com/jupyterhub/jupyterhub/pull/2743) ([@minrk](https://github.com/minrk)) - block urllib3 versions with encoding bug [#2743](https://github.com/jupyterhub/jupyterhub/pull/2743) ([@minrk](https://github.com/minrk))
- Remove tornado deprecated/unnecessary AsyncIOMainLoop().install() call [#2740](https://github.com/jupyterhub/jupyterhub/pull/2740) ([@kinow](https://github.com/kinow)) - Remove tornado deprecated/unnecessary AsyncIOMainLoop().install() call [#2740](https://github.com/jupyterhub/jupyterhub/pull/2740) ([@kinow](https://github.com/kinow))
- Fix deprecated call [#2739](https://github.com/jupyterhub/jupyterhub/pull/2739) ([@kinow](https://github.com/kinow)) - Fix deprecated call [#2739](https://github.com/jupyterhub/jupyterhub/pull/2739) ([@kinow](https://github.com/kinow))
- Remove duplicate hub and authenticator traitlets from Spawner [#2736](https://github.com/jupyterhub/jupyterhub/pull/2736) ([@eslavich](https://github.com/eslavich)) - Remove duplicate hub and authenticator traitlets from Spawner [#2736](https://github.com/jupyterhub/jupyterhub/pull/2736) ([@eslavich](https://github.com/eslavich))
@@ -231,8 +233,8 @@ whether it was through discussion, testing, documentation, or development.
This hook may transform the return value of `Authenticator.authenticate()` This hook may transform the return value of `Authenticator.authenticate()`
and return a new authentication dictionary, and return a new authentication dictionary,
e.g. specifying admin privileges, group membership, e.g. specifying admin privileges, group membership,
or custom white/blacklisting logic. or custom allowed/blocked logic.
This hook is called *after* existing normalization and whitelist checking. This hook is called *after* existing normalization and allowed-username checking.
- `Spawner.options_from_form` may now be async - `Spawner.options_from_form` may now be async
- Added `JupyterHub.shutdown_on_logout` option to trigger shutdown of a user's - Added `JupyterHub.shutdown_on_logout` option to trigger shutdown of a user's
servers when they log out. servers when they log out.
@@ -418,7 +420,7 @@ and tornado < 5.0.
launching an IPython session connected to your JupyterHub database. launching an IPython session connected to your JupyterHub database.
- Include `User.auth_state` in user model on single-user REST endpoints for admins only. - Include `User.auth_state` in user model on single-user REST endpoints for admins only.
- Include `Server.state` in server model on REST endpoints for admins only. - Include `Server.state` in server model on REST endpoints for admins only.
- Add `Authenticator.blacklist` for blacklisting users instead of whitelisting. - Add `Authenticator.blacklist` for blocking users instead of allowing.
- Pass `c.JupyterHub.tornado_settings['cookie_options']` down to Spawners - Pass `c.JupyterHub.tornado_settings['cookie_options']` down to Spawners
so that cookie options (e.g. `expires_days`) can be set globally for the whole application. so that cookie options (e.g. `expires_days`) can be set globally for the whole application.
- SIGINFO (`ctrl-t`) handler showing the current status of all running threads, - SIGINFO (`ctrl-t`) handler showing the current status of all running threads,

View File

@@ -4,23 +4,23 @@ The default Authenticator uses [PAM][] to authenticate system users with
their username and password. With the default Authenticator, any user their username and password. With the default Authenticator, any user
with an account and password on the system will be allowed to login. with an account and password on the system will be allowed to login.
## Create a whitelist of users ## Create a set of allowed users
You can restrict which users are allowed to login with a whitelist, You can restrict which users are allowed to login with a set,
`Authenticator.whitelist`: `Authenticator.allowed_users`:
```python ```python
c.Authenticator.whitelist = {'mal', 'zoe', 'inara', 'kaylee'} c.Authenticator.allowed_users = {'mal', 'zoe', 'inara', 'kaylee'}
``` ```
Users in the whitelist are added to the Hub database when the Hub is Users in the `allowed_users` set are added to the Hub database when the Hub is
started. started.
## Configure admins (`admin_users`) ## Configure admins (`admin_users`)
Admin users of JupyterHub, `admin_users`, can add and remove users from Admin users of JupyterHub, `admin_users`, can add and remove users from
the user `whitelist`. `admin_users` can take actions on other users' the user `allowed_users` set. `admin_users` can take actions on other users'
behalf, such as stopping and restarting their servers. behalf, such as stopping and restarting their servers.
A set of initial admin users, `admin_users` can configured be as follows: A set of initial admin users, `admin_users` can configured be as follows:
@@ -28,7 +28,7 @@ A set of initial admin users, `admin_users` can configured be as follows:
```python ```python
c.Authenticator.admin_users = {'mal', 'zoe'} c.Authenticator.admin_users = {'mal', 'zoe'}
``` ```
Users in the admin list are automatically added to the user `whitelist`, Users in the admin set are automatically added to the user `allowed_users` set,
if they are not already present. if they are not already present.
Each authenticator may have different ways of determining whether a user is an Each authenticator may have different ways of determining whether a user is an
@@ -53,12 +53,12 @@ sure your users know if admin_access is enabled.**
Users can be added to and removed from the Hub via either the admin Users can be added to and removed from the Hub via either the admin
panel or the REST API. When a user is **added**, the user will be panel or the REST API. When a user is **added**, the user will be
automatically added to the whitelist and database. Restarting the Hub automatically added to the allowed users set and database. Restarting the Hub
will not require manually updating the whitelist in your config file, will not require manually updating the allowed users set in your config file,
as the users will be loaded from the database. as the users will be loaded from the database.
After starting the Hub once, it is not sufficient to **remove** a user After starting the Hub once, it is not sufficient to **remove** a user
from the whitelist in your config file. You must also remove the user from the allowed users set in your config file. You must also remove the user
from the Hub's database, either by deleting the user from JupyterHub's from the Hub's database, either by deleting the user from JupyterHub's
admin page, or you can clear the `jupyterhub.sqlite` database and start admin page, or you can clear the `jupyterhub.sqlite` database and start
fresh. fresh.

View File

@@ -52,7 +52,7 @@ c.GitHubOAuthenticator.oauth_callback_url = os.environ['OAUTH_CALLBACK_URL']
c.LocalAuthenticator.create_system_users = True c.LocalAuthenticator.create_system_users = True
# specify users and admin # specify users and admin
c.Authenticator.whitelist = {'rgbkrk', 'minrk', 'jhamrick'} c.Authenticator.allowed_users = {'rgbkrk', 'minrk', 'jhamrick'}
c.Authenticator.admin_users = {'jhamrick', 'rgbkrk'} c.Authenticator.admin_users = {'jhamrick', 'rgbkrk'}
# uses the default spawner # uses the default spawner

View File

@@ -57,7 +57,7 @@ To do this we add to `/etc/sudoers` (use `visudo` for safe editing of sudoers):
For example: For example:
```bash ```bash
# comma-separated whitelist of users that can spawn single-user servers # comma-separated list of users that can spawn single-user servers
# this should include all of your Hub users # this should include all of your Hub users
Runas_Alias JUPYTER_USERS = rhea, zoe, wash Runas_Alias JUPYTER_USERS = rhea, zoe, wash

View File

@@ -313,7 +313,7 @@ class MyHandler(HubAuthenticated, web.RequestHandler):
The HubAuth will automatically load the desired configuration from the Service The HubAuth will automatically load the desired configuration from the Service
environment variables. environment variables.
If you want to limit user access, you can whitelist users through either the If you want to limit user access, you can specify allowed users through either the
`.hub_users` attribute or `.hub_groups`. These are sets that check against the `.hub_users` attribute or `.hub_groups`. These are sets that check against the
username and user group list, respectively. If a user matches neither the user username and user group list, respectively. If a user matches neither the user
list nor the group list, they will not be allowed access. If both are left list nor the group list, they will not be allowed access. If both are left

View File

@@ -7,8 +7,8 @@ problem and how to resolve it.
[*Behavior*](#behavior) [*Behavior*](#behavior)
- JupyterHub proxy fails to start - JupyterHub proxy fails to start
- sudospawner fails to run - sudospawner fails to run
- What is the default behavior when none of the lists (admin, whitelist, - What is the default behavior when none of the lists (admin, allowed,
group whitelist) are set? allowed groups) are set?
- JupyterHub Docker container not accessible at localhost - JupyterHub Docker container not accessible at localhost
[*Errors*](#errors) [*Errors*](#errors)
@@ -55,14 +55,14 @@ or add:
to the config file, `jupyterhub_config.py`. to the config file, `jupyterhub_config.py`.
### What is the default behavior when none of the lists (admin, whitelist, group whitelist) are set? ### What is the default behavior when none of the lists (admin, allowed, allowed groups) are set?
When nothing is given for these lists, there will be no admins, and all users When nothing is given for these lists, there will be no admins, and all users
who can authenticate on the system (i.e. all the unix users on the server with who can authenticate on the system (i.e. all the unix users on the server with
a password) will be allowed to start a server. The whitelist lets you limit a password) will be allowed to start a server. The allowed username set lets you limit
this to a particular set of users, and the admin_users lets you specify who this to a particular set of users, and admin_users lets you specify who
among them may use the admin interface (not necessary, unless you need to do among them may use the admin interface (not necessary, unless you need to do
things like inspect other users' servers, or modify the userlist at runtime). things like inspect other users' servers, or modify the user list at runtime).
### JupyterHub Docker container not accessible at localhost ### JupyterHub Docker container not accessible at localhost
@@ -332,8 +332,7 @@ notebook servers to default to JupyterLab:
### How do I set up JupyterHub for a workshop (when users are not known ahead of time)? ### How do I set up JupyterHub for a workshop (when users are not known ahead of time)?
1. Set up JupyterHub using OAuthenticator for GitHub authentication 1. Set up JupyterHub using OAuthenticator for GitHub authentication
2. Configure whitelist to be an empty list in` jupyterhub_config.py` 2. Configure admin list to have workshop leaders be listed with administrator privileges.
3. Configure admin list to have workshop leaders be listed with administrator privileges.
Users will need a GitHub account to login and be authenticated by the Hub. Users will need a GitHub account to login and be authenticated by the Hub.

View File

@@ -201,7 +201,7 @@ class OAuthAuthorizeHandler(OAuthHandler, BaseHandler):
def needs_oauth_confirm(self, user, oauth_client): def needs_oauth_confirm(self, user, oauth_client):
"""Return whether the given oauth client needs to prompt for access for the given user """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) (i.e. the user's own server)
@@ -214,9 +214,8 @@ class OAuthAuthorizeHandler(OAuthHandler, BaseHandler):
if ( if (
# it's the user's own server # it's the user's own server
oauth_client.identifier in own_oauth_client_ids oauth_client.identifier in own_oauth_client_ids
# or it's in the global whitelist # or it's in the global no-confirm list
or oauth_client.identifier or oauth_client.identifier in self.settings.get('oauth_no_confirm', set())
in self.settings.get('oauth_no_confirm_whitelist', set())
): ):
return False return False
# default: require confirmation # default: require confirmation
@@ -229,7 +228,7 @@ class OAuthAuthorizeHandler(OAuthHandler, BaseHandler):
Render oauth confirmation page: Render oauth confirmation page:
"Server at ... would like permission to ...". "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. will skip confirmation.
""" """

View File

@@ -1689,22 +1689,22 @@ class JupyterHub(Application):
# the admin_users config variable will never be used after this point. # the admin_users config variable will never be used after this point.
# only the database values will be referenced. # only the database values will be referenced.
whitelist = [ allowed_users = [
self.authenticator.normalize_username(name) self.authenticator.normalize_username(name)
for name in self.authenticator.whitelist for name in self.authenticator.allowed_users
] ]
self.authenticator.whitelist = set(whitelist) # force normalization self.authenticator.allowed_users = set(allowed_users) # force normalization
for username in whitelist: for username in allowed_users:
if not self.authenticator.validate_username(username): if not self.authenticator.validate_username(username):
raise ValueError("username %r is not valid" % username) raise ValueError("username %r is not valid" % username)
if not whitelist: if not allowed_users:
self.log.info( self.log.info(
"Not using whitelist. Any authenticated user will be allowed." "Not using allowed_users. Any authenticated user will be allowed."
) )
# add whitelisted users to the db # add allowed users to the db
for name in whitelist: for name in allowed_users:
user = orm.User.find(db, name) user = orm.User.find(db, name)
if user is None: if user is None:
user = orm.User(name=name) user = orm.User(name=name)
@@ -1714,9 +1714,9 @@ class JupyterHub(Application):
db.commit() db.commit()
# Notify authenticator of all users. # Notify authenticator of all users.
# This ensures Auth whitelist is up-to-date with the database. # This ensures Authenticator.allowed_users is up-to-date with the database.
# This lets whitelist be used to set up initial list, # This lets .allowed_users be used to set up initial list,
# but changes to the whitelist can occur in the database, # but changes to the allowed_users set can occur in the database,
# and persist across sessions. # and persist across sessions.
total_users = 0 total_users = 0
for user in db.query(orm.User): for user in db.query(orm.User):
@@ -1753,9 +1753,9 @@ class JupyterHub(Application):
user.created = user.last_activity or datetime.utcnow() user.created = user.last_activity or datetime.utcnow()
db.commit() db.commit()
# The whitelist set and the users in the db are now the same. # The allowed_users set and the users in the db are now the same.
# From this point on, any user changes should be done simultaneously # 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_users set and user db, unless the allowed set is empty (all users allowed).
TOTAL_USERS.set(total_users) TOTAL_USERS.set(total_users)
@@ -1770,11 +1770,11 @@ class JupyterHub(Application):
for username in usernames: for username in usernames:
username = self.authenticator.normalize_username(username) username = self.authenticator.normalize_username(username)
if not ( if not (
await maybe_future( await maybe_future(self.authenticator.check_allowed(username, None))
self.authenticator.check_whitelist(username, None)
)
): ):
raise ValueError("Username %r is not in whitelist" % username) raise ValueError(
"Username %r is not in Authenticator.allowed_users" % username
)
user = orm.User.find(db, name=username) user = orm.User.find(db, name=username)
if user is None: if user is None:
if not self.authenticator.validate_username(username): if not self.authenticator.validate_username(username):
@@ -1798,11 +1798,14 @@ class JupyterHub(Application):
if kind == 'user': if kind == 'user':
name = self.authenticator.normalize_username(name) name = self.authenticator.normalize_username(name)
if not ( 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_users"
% name
)
if not self.authenticator.validate_username(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 kind == 'service':
if not any(service["name"] == name for service in self.services): if not any(service["name"] == name for service in self.services):
self.log.warning( self.log.warning(
@@ -2183,14 +2186,14 @@ class JupyterHub(Application):
else: else:
version_hash = datetime.now().strftime("%Y%m%d%H%M%S") 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(): for service in self._service_map.values():
if service.oauth_no_confirm: if service.oauth_no_confirm:
self.log.warning( self.log.warning(
"Allowing service %s to complete OAuth without confirmation on an authorization web page", "Allowing service %s to complete OAuth without confirmation on an authorization web page",
service.name, service.name,
) )
oauth_no_confirm_whitelist.add(service.oauth_client_id) oauth_no_confirm_list.add(service.oauth_client_id)
settings = dict( settings = dict(
log_function=log_request, log_function=log_request,
@@ -2226,7 +2229,7 @@ class JupyterHub(Application):
default_server_name=self._default_server_name, default_server_name=self._default_server_name,
named_server_limit_per_user=self.named_server_limit_per_user, named_server_limit_per_user=self.named_server_limit_per_user,
oauth_provider=self.oauth_provider, 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, concurrent_spawn_limit=self.concurrent_spawn_limit,
spawn_throttle_retry_range=self.spawn_throttle_retry_range, spawn_throttle_retry_range=self.spawn_throttle_retry_range,
active_server_limit=self.active_server_limit, active_server_limit=self.active_server_limit,

View File

@@ -7,6 +7,7 @@ import re
import sys import sys
import warnings import warnings
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from functools import partial
from shutil import which from shutil import which
from subprocess import PIPE from subprocess import PIPE
from subprocess import Popen from subprocess import Popen
@@ -100,41 +101,74 @@ class Authenticator(LoggingConfigurable):
""" """
).tag(config=True) ).tag(config=True)
whitelist = Set( whitelist = Set(help="Deprecated, use `Authenticator.allowed_users`", config=True,)
allowed_users = Set(
help=""" 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 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. authenticator has in place.
If empty, does not perform any additional restriction. If empty, does not perform any additional restriction.
.. versionchanged:: 1.2
`Authenticator.whitelist` renamed to `allowed_users`
""" """
).tag(config=True) ).tag(config=True)
blacklist = Set( blocked_users = Set(
help=""" 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 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. authenticator has in place.
If empty, does not perform any additional restriction. If empty, does not perform any additional restriction.
.. versionadded: 0.9 .. versionadded: 0.9
.. versionchanged:: 1.2
`Authenticator.blacklist` renamed to `blocked_users`
""" """
).tag(config=True) ).tag(config=True)
@observe('whitelist') _deprecated_aliases = {
def _check_whitelist(self, change): "whitelist": ("allowed_users", "1.2"),
"blacklist": ("blocked_users", "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_users')
def _check_allowed_users(self, change):
short_names = [name for name in change['new'] if len(name) <= 1] short_names = [name for name in change['new'] if len(name) <= 1]
if short_names: if short_names:
sorted_names = sorted(short_names) sorted_names = sorted(short_names)
single = ''.join(sorted_names) single = ''.join(sorted_names)
string_set_typo = "set('%s')" % single string_set_typo = "set('%s')" % single
self.log.warning( self.log.warning(
"whitelist contains single-character names: %s; did you mean set([%r]) instead of %s?", "Allowed set contains single-character names: %s; did you mean set([%r]) instead of %s?",
sorted_names[:8], sorted_names[:8],
single, single,
string_set_typo, string_set_typo,
@@ -261,39 +295,74 @@ class Authenticator(LoggingConfigurable):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
for method_name in ( self._init_deprecated_methods()
'check_whitelist',
'check_blacklist', def _init_deprecated_methods(self):
'check_group_whitelist', # handles deprecated signature *and* name
# with correct subclass override priority!
for old_name, new_name in (
('check_whitelist', 'check_allowed'),
('check_blacklist', 'check_blocked_users'),
('check_group_whitelist', 'check_allowed_groups'),
): ):
original_method = getattr(self, method_name, None) old_method = getattr(self, old_name, None)
if original_method is None: if old_method is None:
# no such method (check_group_whitelist is optional) # no such method (check_group_whitelist is optional)
continue continue
signature = inspect.signature(original_method)
if 'authentication' not in signature.parameters: # allow old name to have higher priority
# if and only if it's defined in a later subclass
# than the new name
for cls in self.__class__.mro():
has_old_name = old_name in cls.__dict__
has_new_name = new_name in cls.__dict__
if has_new_name:
break
if has_old_name and not has_new_name:
warnings.warn(
"{0}.{1} should be renamed to {0}.{2} for JupyterHub >= 1.2".format(
cls.__name__, old_name, new_name
),
DeprecationWarning,
)
# use old name instead of new
# if old name is overridden in subclass
def _new_calls_old(old_name, *args, **kwargs):
return getattr(self, old_name)(*args, **kwargs)
setattr(self, new_name, partial(_new_calls_old, old_name))
break
# deprecate pre-1.0 method signatures
signature = inspect.signature(old_method)
if 'authentication' not in signature.parameters and not any(
param.kind == inspect.Parameter.VAR_KEYWORD
for param in signature.parameters.values()
):
# adapt to pre-1.0 signature for compatibility # adapt to pre-1.0 signature for compatibility
warnings.warn( warnings.warn(
""" """
{0}.{1} does not support the authentication argument, {0}.{1} does not support the authentication argument,
added in JupyterHub 1.0. added in JupyterHub 1.0. and is renamed to {2} in JupyterHub 1.2.
It should have the signature: It should have the signature:
def {1}(self, username, authentication=None): def {2}(self, username, authentication=None):
... ...
Adapting for compatibility. Adapting for compatibility.
""".format( """.format(
self.__class__.__name__, method_name self.__class__.__name__, old_name, new_name
), ),
DeprecationWarning, DeprecationWarning,
) )
def wrapped_method(username, authentication=None, **kwargs): def wrapped_method(
original_method, username, authentication=None, **kwargs
):
return original_method(username, **kwargs) return original_method(username, **kwargs)
setattr(self, method_name, wrapped_method) setattr(self, old_name, partial(wrapped_method, old_method))
async def run_post_auth_hook(self, handler, authentication): async def run_post_auth_hook(self, handler, authentication):
""" """
@@ -327,39 +396,45 @@ class Authenticator(LoggingConfigurable):
username = self.username_map.get(username, username) username = self.username_map.get(username, username)
return username return username
def check_whitelist(self, username, authentication=None): def check_allowed(self, username, authentication=None):
"""Check if a username is allowed to authenticate based on whitelist configuration """Check if a username is allowed to authenticate based on configuration
Return True if username is allowed, False otherwise. Return True if username is allowed, False otherwise.
No whitelist means any username is allowed. No allowed_users 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 .. versionchanged:: 1.0
Signature updated to accept authentication data and any future changes 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): .. versionchanged:: 1.2
"""Check if a username is blocked to authenticate based on blacklist configuration Renamed check_whitelist to check_allowed
"""
if not self.allowed_users:
# No allowed set means any name is allowed
return True
return username in self.allowed_users
def check_blocked_users(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. 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 .. versionadded: 0.9
.. versionchanged:: 1.0 .. versionchanged:: 1.0
Signature updated to accept authentication data as second argument Signature updated to accept authentication data as second argument
.. versionchanged:: 1.2
Renamed check_blacklist to check_blocked_users
""" """
if not self.blacklist: if not self.blocked_users:
# No blacklist means any name is allowed # No block list means any name is allowed
return True return True
return username not in self.blacklist return username not in self.blocked_users
async def get_authenticated_user(self, handler, data): async def get_authenticated_user(self, handler, data):
"""Authenticate the user who is attempting to log in """Authenticate the user who is attempting to log in
@@ -368,7 +443,7 @@ class Authenticator(LoggingConfigurable):
This calls `authenticate`, which should be overridden in subclasses, This calls `authenticate`, which should be overridden in subclasses,
normalizes the username if any normalization should be done, 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. This is the outer API for authenticating a user.
Subclasses should not override this method. Subclasses should not override this method.
@@ -376,7 +451,7 @@ class Authenticator(LoggingConfigurable):
The various stages can be overridden separately: The various stages can be overridden separately:
- `authenticate` turns formdata into a username - `authenticate` turns formdata into a username
- `normalize_username` normalizes the username - `normalize_username` normalizes the username
- `check_whitelist` checks against the user whitelist - `check_allowed` checks against the allowed usernames
.. versionchanged:: 0.8 .. versionchanged:: 0.8
return dict instead of username return dict instead of username
@@ -390,7 +465,7 @@ class Authenticator(LoggingConfigurable):
else: else:
authenticated = {'name': authenticated} authenticated = {'name': authenticated}
authenticated.setdefault('auth_state', None) 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) authenticated.setdefault('admin', None)
# normalize the username # normalize the username
@@ -401,20 +476,18 @@ class Authenticator(LoggingConfigurable):
self.log.warning("Disallowing invalid username %r.", username) self.log.warning("Disallowing invalid username %r.", username)
return return
blacklist_pass = await maybe_future( blocked_pass = await maybe_future(
self.check_blacklist(username, authenticated) self.check_blocked_users(username, authenticated)
)
whitelist_pass = await maybe_future(
self.check_whitelist(username, authenticated)
) )
allowed_pass = await maybe_future(self.check_allowed(username, authenticated))
if blacklist_pass: if blocked_pass:
pass pass
else: else:
self.log.warning("User %r in blacklist. Stop authentication", username) self.log.warning("User %r blocked. Stop authentication", username)
return return
if whitelist_pass: if allowed_pass:
if authenticated['admin'] is None: if authenticated['admin'] is None:
authenticated['admin'] = await maybe_future( authenticated['admin'] = await maybe_future(
self.is_admin(handler, authenticated) self.is_admin(handler, authenticated)
@@ -424,7 +497,7 @@ class Authenticator(LoggingConfigurable):
return authenticated return authenticated
else: else:
self.log.warning("User %r not in whitelist.", username) self.log.warning("User %r not allowed.", username)
return return
async def refresh_user(self, user, handler=None): async def refresh_user(self, user, handler=None):
@@ -480,7 +553,7 @@ class Authenticator(LoggingConfigurable):
It must return the username on successful authentication, It must return the username on successful authentication,
and return None on failed authentication. and return None on failed authentication.
Checking the whitelist is handled separately by the caller. Checking allowed_users/blocked_users is handled separately by the caller.
.. versionchanged:: 0.8 .. versionchanged:: 0.8
Allow `authenticate` to return a dict containing auth_state. Allow `authenticate` to return a dict containing auth_state.
@@ -521,10 +594,10 @@ class Authenticator(LoggingConfigurable):
This method may be a coroutine. 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_users set.
Subclasses may do more extensive things, such as adding actual unix users, 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_users set is updated.
Note that this should be idempotent, since it is called whenever the hub restarts Note that this should be idempotent, since it is called whenever the hub restarts
for all users. for all users.
@@ -534,19 +607,19 @@ class Authenticator(LoggingConfigurable):
""" """
if not self.validate_username(user.name): if not self.validate_username(user.name):
raise ValueError("Invalid username: %s" % user.name) raise ValueError("Invalid username: %s" % user.name)
if self.whitelist: if self.allowed_users:
self.whitelist.add(user.name) self.allowed_users.add(user.name)
def delete_user(self, user): def delete_user(self, user):
"""Hook called when a user is deleted """Hook called when a user is deleted
Removes the user from the whitelist. Removes the user from the allowed_users set.
Subclasses should call super to ensure the whitelist is updated. Subclasses should call super to ensure the allowed_users set is updated.
Args: Args:
user (User): The User wrapper object user (User): The User wrapper object
""" """
self.whitelist.discard(user.name) self.allowed_users.discard(user.name)
auto_login = Bool( auto_login = Bool(
False, False,
@@ -611,6 +684,41 @@ class Authenticator(LoggingConfigurable):
return [('/login', LoginHandler)] return [('/login', LoginHandler)]
def _deprecated_method(old_name, new_name, version):
"""Create a deprecated method wrapper for a deprecated method name"""
def deprecated(self, *args, **kwargs):
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)
return deprecated
import types
# deprecate white/blacklist method names
for _old_name, _new_name, _version in [
("check_whitelist", "check_allowed", "1.2"),
("check_blacklist", "check_blocked_users", "1.2"),
]:
setattr(
Authenticator, _old_name, _deprecated_method(_old_name, _new_name, _version),
)
class LocalAuthenticator(Authenticator): class LocalAuthenticator(Authenticator):
"""Base class for Authenticators that work with local Linux/UNIX users """Base class for Authenticators that work with local Linux/UNIX users
@@ -670,37 +778,37 @@ class LocalAuthenticator(Authenticator):
""" """
).tag(config=True) ).tag(config=True)
group_whitelist = Set( group_whitelist = Set(help="""DEPRECATED: use allowed_groups""",).tag(config=True)
help="""
Whitelist all users from this UNIX group.
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) ).tag(config=True)
@observe('group_whitelist') @observe('allowed_groups')
def _group_whitelist_changed(self, change): def _allowed_groups_changed(self, change):
""" """Log a warning if mutually exclusive user and group allowed sets are specified."""
Log a warning if both group_whitelist and user whitelist are set. if self.allowed_users:
"""
if self.whitelist:
self.log.warning( self.log.warning(
"Ignoring username whitelist because group whitelist supplied!" "Ignoring Authenticator.allowed_users set because Authenticator.allowed_groups supplied!"
) )
def check_whitelist(self, username, authentication=None): def check_allowed(self, username, authentication=None):
if self.group_whitelist: if self.allowed_groups:
return self.check_group_whitelist(username, authentication) return self.check_allowed_groups(username, authentication)
else: 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 return False
for grnam in self.group_whitelist: for grnam in self.allowed_groups:
try: try:
group = self._getgrnam(grnam) group = self._getgrnam(grnam)
except KeyError: except KeyError:
@@ -844,7 +952,7 @@ class PAMAuthenticator(LocalAuthenticator):
Authoritative list of user groups that determine admin access. Authoritative list of user groups that determine admin access.
Users not in these groups can still be granted admin status through admin_users. 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) ).tag(config=True)
@@ -987,6 +1095,16 @@ class PAMAuthenticator(LocalAuthenticator):
return super().normalize_username(username) return super().normalize_username(username)
for _old_name, _new_name, _version in [
("check_group_whitelist", "check_group_allowed", "1.2"),
]:
setattr(
LocalAuthenticator,
_old_name,
_deprecated_method(_old_name, _new_name, _version),
)
class DummyAuthenticator(Authenticator): class DummyAuthenticator(Authenticator):
"""Dummy Authenticator for testing """Dummy Authenticator for testing

View File

@@ -860,15 +860,15 @@ class HubAuthenticated(object):
if kind == 'service': if kind == 'service':
# it's a service, check hub_services # it's a service, check hub_services
if self.hub_services and name in self.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 return model
else: else:
app_log.warning("Not allowing Hub service %s", name) app_log.warning("Not allowing Hub service %s", name)
raise UserNotAllowed(model) raise UserNotAllowed(model)
if self.hub_users and name in self.hub_users: if self.hub_users and name in self.hub_users:
# user in whitelist # user in allowed list
app_log.debug("Allowing whitelisted Hub user %s", name) app_log.debug("Allowing Hub user %s", name)
return model return model
elif self.hub_groups and set(model['groups']).intersection(self.hub_groups): elif self.hub_groups and set(model['groups']).intersection(self.hub_groups):
allowed_groups = set(model['groups']).intersection(self.hub_groups) allowed_groups = set(model['groups']).intersection(self.hub_groups)
@@ -877,7 +877,7 @@ class HubAuthenticated(object):
name, name,
','.join(sorted(allowed_groups)), ','.join(sorted(allowed_groups)),
) )
# group in whitelist # group in allowed list
return model return model
else: else:
app_log.warning("Not allowing Hub user %s", name) app_log.warning("Not allowing Hub user %s", name)

View File

@@ -435,9 +435,9 @@ class Spawner(LoggingConfigurable):
'LC_ALL', 'LC_ALL',
], ],
help=""" 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. (such as `CONFIGPROXY_AUTH_TOKEN`) is not passed to the single-user server's process.
""", """,
).tag(config=True) ).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: Environment variables that end up in the single-user server's process come from 3 sources:
- This `environment` configurable - 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) - 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 The `environment` configurable should be set by JupyterHub administrators to add

View File

@@ -93,7 +93,7 @@ def test_generate_config():
os.remove(cfg_file) os.remove(cfg_file)
assert cfg_file in out assert cfg_file in out
assert 'Spawner.cmd' in cfg_text assert 'Spawner.cmd' in cfg_text
assert 'Authenticator.whitelist' in cfg_text assert 'Authenticator.allowed_users' in cfg_text
async def test_init_tokens(request): async def test_init_tokens(request):

View File

@@ -1,11 +1,14 @@
"""Tests for PAM authentication""" """Tests for PAM authentication"""
# Copyright (c) Jupyter Development Team. # Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License. # Distributed under the terms of the Modified BSD License.
import logging
import os import os
import warnings
from unittest import mock from unittest import mock
import pytest import pytest
from requests import HTTPError from requests import HTTPError
from traitlets.config import Config
from .mocking import MockPAMAuthenticator from .mocking import MockPAMAuthenticator
from .mocking import MockStructGroup from .mocking import MockStructGroup
@@ -137,8 +140,8 @@ async def test_pam_auth_admin_groups():
assert authorized['admin'] is False assert authorized['admin'] is False
async def test_pam_auth_whitelist(): async def test_pam_auth_allowed():
authenticator = MockPAMAuthenticator(whitelist={'wash', 'kaylee'}) authenticator = MockPAMAuthenticator(allowed_users={'wash', 'kaylee'})
authorized = await authenticator.get_authenticated_user( authorized = await authenticator.get_authenticated_user(
None, {'username': 'kaylee', 'password': 'kaylee'} None, {'username': 'kaylee', 'password': 'kaylee'}
) )
@@ -155,11 +158,11 @@ async def test_pam_auth_whitelist():
assert authorized is None assert authorized is None
async def test_pam_auth_group_whitelist(): async def test_pam_auth_allowed_groups():
def getgrnam(name): def getgrnam(name):
return MockStructGroup('grp', ['kaylee']) return MockStructGroup('grp', ['kaylee'])
authenticator = MockPAMAuthenticator(group_whitelist={'group'}) authenticator = MockPAMAuthenticator(allowed_groups={'group'})
with mock.patch.object(authenticator, '_getgrnam', getgrnam): with mock.patch.object(authenticator, '_getgrnam', getgrnam):
authorized = await authenticator.get_authenticated_user( authorized = await authenticator.get_authenticated_user(
@@ -174,7 +177,7 @@ async def test_pam_auth_group_whitelist():
assert authorized is None assert authorized is None
async def test_pam_auth_blacklist(): async def test_pam_auth_blocked():
# Null case compared to next case # Null case compared to next case
authenticator = MockPAMAuthenticator() authenticator = MockPAMAuthenticator()
authorized = await authenticator.get_authenticated_user( authorized = await authenticator.get_authenticated_user(
@@ -183,33 +186,33 @@ async def test_pam_auth_blacklist():
assert authorized['name'] == 'wash' assert authorized['name'] == 'wash'
# Blacklist basics # Blacklist basics
authenticator = MockPAMAuthenticator(blacklist={'wash'}) authenticator = MockPAMAuthenticator(blocked_users={'wash'})
authorized = await authenticator.get_authenticated_user( authorized = await authenticator.get_authenticated_user(
None, {'username': 'wash', 'password': 'wash'} None, {'username': 'wash', 'password': 'wash'}
) )
assert authorized is None assert authorized is None
# User in both white and blacklists: default deny. Make error someday? # User in both allowed and blocked: default deny. Make error someday?
authenticator = MockPAMAuthenticator( authenticator = MockPAMAuthenticator(
blacklist={'wash'}, whitelist={'wash', 'kaylee'} blocked_users={'wash'}, allowed_users={'wash', 'kaylee'}
) )
authorized = await authenticator.get_authenticated_user( authorized = await authenticator.get_authenticated_user(
None, {'username': 'wash', 'password': 'wash'} None, {'username': 'wash', 'password': 'wash'}
) )
assert authorized is None assert authorized is None
# User not in blacklist can log in # User not in blocked set can log in
authenticator = MockPAMAuthenticator( authenticator = MockPAMAuthenticator(
blacklist={'wash'}, whitelist={'wash', 'kaylee'} blocked_users={'wash'}, allowed_users={'wash', 'kaylee'}
) )
authorized = await authenticator.get_authenticated_user( authorized = await authenticator.get_authenticated_user(
None, {'username': 'kaylee', 'password': 'kaylee'} None, {'username': 'kaylee', 'password': 'kaylee'}
) )
assert authorized['name'] == 'kaylee' assert authorized['name'] == 'kaylee'
# User in whitelist, blacklist irrelevent # User in allowed, blocked irrelevent
authenticator = MockPAMAuthenticator( authenticator = MockPAMAuthenticator(
blacklist={'mal'}, whitelist={'wash', 'kaylee'} blocked_users={'mal'}, allowed_users={'wash', 'kaylee'}
) )
authorized = await authenticator.get_authenticated_user( authorized = await authenticator.get_authenticated_user(
None, {'username': 'wash', 'password': 'wash'} None, {'username': 'wash', 'password': 'wash'}
@@ -218,15 +221,16 @@ async def test_pam_auth_blacklist():
# User in neither list # User in neither list
authenticator = MockPAMAuthenticator( authenticator = MockPAMAuthenticator(
blacklist={'mal'}, whitelist={'wash', 'kaylee'} blocked_users={'mal'}, allowed_users={'wash', 'kaylee'}
) )
authorized = await authenticator.get_authenticated_user( authorized = await authenticator.get_authenticated_user(
None, {'username': 'simon', 'password': 'simon'} None, {'username': 'simon', 'password': 'simon'}
) )
assert authorized is None assert authorized is None
# blacklist == {} authenticator = MockPAMAuthenticator(
authenticator = MockPAMAuthenticator(blacklist=set(), whitelist={'wash', 'kaylee'}) blocked_users=set(), allowed_users={'wash', 'kaylee'}
)
authorized = await authenticator.get_authenticated_user( authorized = await authenticator.get_authenticated_user(
None, {'username': 'kaylee', 'password': 'kaylee'} None, {'username': 'kaylee', 'password': 'kaylee'}
) )
@@ -253,7 +257,7 @@ async def test_deprecated_signatures():
async def test_pam_auth_no_such_group(): async def test_pam_auth_no_such_group():
authenticator = MockPAMAuthenticator(group_whitelist={'nosuchcrazygroup'}) authenticator = MockPAMAuthenticator(allowed_groups={'nosuchcrazygroup'})
authorized = await authenticator.get_authenticated_user( authorized = await authenticator.get_authenticated_user(
None, {'username': 'kaylee', 'password': 'kaylee'} None, {'username': 'kaylee', 'password': 'kaylee'}
) )
@@ -262,7 +266,7 @@ async def test_pam_auth_no_such_group():
async def test_wont_add_system_user(): async def test_wont_add_system_user():
user = orm.User(name='lioness4321') user = orm.User(name='lioness4321')
authenticator = auth.PAMAuthenticator(whitelist={'mal'}) authenticator = auth.PAMAuthenticator(allowed_users={'mal'})
authenticator.create_system_users = False authenticator.create_system_users = False
with pytest.raises(KeyError): with pytest.raises(KeyError):
await authenticator.add_user(user) await authenticator.add_user(user)
@@ -270,7 +274,7 @@ async def test_wont_add_system_user():
async def test_cant_add_system_user(): async def test_cant_add_system_user():
user = orm.User(name='lioness4321') user = orm.User(name='lioness4321')
authenticator = auth.PAMAuthenticator(whitelist={'mal'}) authenticator = auth.PAMAuthenticator(allowed_users={'mal'})
authenticator.add_user_cmd = ['jupyterhub-fake-command'] authenticator.add_user_cmd = ['jupyterhub-fake-command']
authenticator.create_system_users = True authenticator.create_system_users = True
@@ -296,7 +300,7 @@ async def test_cant_add_system_user():
async def test_add_system_user(): async def test_add_system_user():
user = orm.User(name='lioness4321') user = orm.User(name='lioness4321')
authenticator = auth.PAMAuthenticator(whitelist={'mal'}) authenticator = auth.PAMAuthenticator(allowed_users={'mal'})
authenticator.create_system_users = True authenticator.create_system_users = True
authenticator.add_user_cmd = ['echo', '/home/USERNAME'] authenticator.add_user_cmd = ['echo', '/home/USERNAME']
@@ -317,13 +321,13 @@ async def test_add_system_user():
async def test_delete_user(): async def test_delete_user():
user = orm.User(name='zoe') user = orm.User(name='zoe')
a = MockPAMAuthenticator(whitelist={'mal'}) a = MockPAMAuthenticator(allowed_users={'mal'})
assert 'zoe' not in a.whitelist assert 'zoe' not in a.allowed_users
await a.add_user(user) await a.add_user(user)
assert 'zoe' in a.whitelist assert 'zoe' in a.allowed_users
a.delete_user(user) a.delete_user(user)
assert 'zoe' not in a.whitelist assert 'zoe' not in a.allowed_users
def test_urls(): def test_urls():
@@ -461,3 +465,55 @@ async def test_post_auth_hook():
) )
assert authorized['testkey'] == 'testvalue' assert authorized['testkey'] == 'testvalue'
class MyAuthenticator(auth.Authenticator):
def check_whitelist(self, username, authentication=None):
return username == "subclass-allowed"
def test_deprecated_config(caplog):
cfg = Config()
cfg.Authenticator.whitelist = {'user'}
log = logging.getLogger("testlog")
authenticator = auth.Authenticator(config=cfg, log=log)
assert caplog.record_tuples == [
(
log.name,
logging.WARNING,
'Authenticator.whitelist is deprecated in JupyterHub 1.2, use '
'Authenticator.allowed_users instead',
)
]
assert authenticator.allowed_users == {'user'}
def test_deprecated_methods():
cfg = Config()
cfg.Authenticator.whitelist = {'user'}
authenticator = auth.Authenticator(config=cfg)
assert authenticator.check_allowed("user")
with pytest.deprecated_call():
assert authenticator.check_whitelist("user")
assert not authenticator.check_allowed("otheruser")
with pytest.deprecated_call():
assert not authenticator.check_whitelist("otheruser")
def test_deprecated_config_subclass():
cfg = Config()
cfg.MyAuthenticator.whitelist = {'user'}
with pytest.deprecated_call():
authenticator = MyAuthenticator(config=cfg)
assert authenticator.allowed_users == {'user'}
def test_deprecated_methods_subclass():
with pytest.deprecated_call():
authenticator = MyAuthenticator()
assert authenticator.check_allowed("subclass-allowed")
assert authenticator.check_whitelist("subclass-allowed")
assert not authenticator.check_allowed("otheruser")
assert not authenticator.check_whitelist("otheruser")

View File

@@ -744,7 +744,7 @@ async def test_shutdown_on_logout(app, shutdown_on_logout):
assert spawner.ready == (not 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 auth = app.authenticator
mock_add_user = mock.Mock() mock_add_user = mock.Mock()
with mock.patch.object(auth, 'add_user', mock_add_user): with mock.patch.object(auth, 'add_user', mock_add_user):

View File

@@ -185,7 +185,7 @@ def test_hub_authenticated(request):
m.get(good_url, text=json.dumps(mock_model)) m.get(good_url, text=json.dumps(mock_model))
# no whitelist # no specific allowed user
r = requests.get( r = requests.get(
'http://127.0.0.1:%i' % port, 'http://127.0.0.1:%i' % port,
cookies={'jubal': 'early'}, cookies={'jubal': 'early'},
@@ -194,7 +194,7 @@ def test_hub_authenticated(request):
r.raise_for_status() r.raise_for_status()
assert r.status_code == 200 assert r.status_code == 200
# pass whitelist # pass allowed user
TestHandler.hub_users = {'jubalearly'} TestHandler.hub_users = {'jubalearly'}
r = requests.get( r = requests.get(
'http://127.0.0.1:%i' % port, 'http://127.0.0.1:%i' % port,
@@ -204,7 +204,7 @@ def test_hub_authenticated(request):
r.raise_for_status() r.raise_for_status()
assert r.status_code == 200 assert r.status_code == 200
# no pass whitelist # no pass allowed ser
TestHandler.hub_users = {'kaylee'} TestHandler.hub_users = {'kaylee'}
r = requests.get( r = requests.get(
'http://127.0.0.1:%i' % port, 'http://127.0.0.1:%i' % port,
@@ -213,7 +213,7 @@ def test_hub_authenticated(request):
) )
assert r.status_code == 403 assert r.status_code == 403
# pass group whitelist # pass allowed group
TestHandler.hub_groups = {'lions'} TestHandler.hub_groups = {'lions'}
r = requests.get( r = requests.get(
'http://127.0.0.1:%i' % port, 'http://127.0.0.1:%i' % port,
@@ -223,7 +223,7 @@ def test_hub_authenticated(request):
r.raise_for_status() r.raise_for_status()
assert r.status_code == 200 assert r.status_code == 200
# no pass group whitelist # no pass allowed group
TestHandler.hub_groups = {'tigers'} TestHandler.hub_groups = {'tigers'}
r = requests.get( r = requests.get(
'http://127.0.0.1:%i' % port, 'http://127.0.0.1:%i' % port,