mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-14 13:33:00 +00:00
validate usernames
via Authenticator.validate_username base class configurable with Authenticator.username_pattern
This commit is contained in:
@@ -33,14 +33,25 @@ class UserListAPIHandler(APIHandler):
|
||||
admin = data.get('admin', False)
|
||||
|
||||
to_create = []
|
||||
invalid_names = []
|
||||
for name in usernames:
|
||||
name = self.authenticator.normalize_username(name)
|
||||
if not self.authenticator.validate_username(name):
|
||||
invalid_names.append(name)
|
||||
continue
|
||||
user = self.find_user(name)
|
||||
if user is not None:
|
||||
self.log.warn("User %s already exists" % name)
|
||||
else:
|
||||
to_create.append(name)
|
||||
|
||||
if invalid_names:
|
||||
if len(invalid_names) == 1:
|
||||
msg = "Invalid username: %s" % invalid_names[0]
|
||||
else:
|
||||
msg = "Invalid usernames: %s" % ', '.join(invalid_names)
|
||||
raise web.HTTPError(400, msg)
|
||||
|
||||
if not to_create:
|
||||
raise web.HTTPError(400, "All %i users already exist" % len(usernames))
|
||||
|
||||
|
@@ -634,6 +634,9 @@ class JupyterHub(Application):
|
||||
self.authenticator.normalize_username(name)
|
||||
for name in self.authenticator.admin_users
|
||||
]
|
||||
for username in admin_users:
|
||||
if not self.authenticator.validate_username(username):
|
||||
raise ValueError("username %r is not valid" % username)
|
||||
|
||||
if not admin_users:
|
||||
self.log.warning("No admin users, admin interface will be unavailable.")
|
||||
@@ -658,6 +661,9 @@ class JupyterHub(Application):
|
||||
self.authenticator.normalize_username(name)
|
||||
for name in self.authenticator.whitelist
|
||||
]
|
||||
for username in whitelist:
|
||||
if not self.authenticator.validate_username(username):
|
||||
raise ValueError("username %r is not valid" % username)
|
||||
|
||||
if not whitelist:
|
||||
self.log.info("Not using whitelist. Any authenticated user will be allowed.")
|
||||
|
@@ -14,7 +14,7 @@ from tornado import gen
|
||||
import pamela
|
||||
|
||||
from traitlets.config import LoggingConfigurable
|
||||
from traitlets import Bool, Set, Unicode, Any
|
||||
from traitlets import Bool, Set, Unicode, Dict, Any
|
||||
|
||||
from .handlers.login import LoginHandler
|
||||
from .utils import url_path_join
|
||||
@@ -53,6 +53,28 @@ class Authenticator(LoggingConfigurable):
|
||||
"""
|
||||
)
|
||||
|
||||
username_pattern = Unicode(config=True,
|
||||
help="""Regular expression pattern for validating usernames.
|
||||
|
||||
If not defined: allow any username.
|
||||
"""
|
||||
)
|
||||
def _username_pattern_changed(self, name, old, new):
|
||||
if not new:
|
||||
self.username_regex = None
|
||||
self.username_regex = re.compile(new)
|
||||
|
||||
username_regex = Any()
|
||||
|
||||
def validate_username(self, username):
|
||||
"""Validate a (normalized) username.
|
||||
|
||||
Return True if username is valid, False otherwise.
|
||||
"""
|
||||
if not self.username_regex:
|
||||
return True
|
||||
return bool(self.username_regex.match(username))
|
||||
|
||||
username_map = Dict(config=True,
|
||||
help="""Dictionary mapping authenticator usernames to JupyterHub users.
|
||||
|
||||
@@ -144,6 +166,8 @@ class Authenticator(LoggingConfigurable):
|
||||
Subclasses may do more extensive things,
|
||||
such as adding actual unix users.
|
||||
"""
|
||||
if not self.validate_username(user.name):
|
||||
raise ValueError("Invalid username: %s" % user.name)
|
||||
if self.whitelist:
|
||||
self.whitelist.add(user.name)
|
||||
|
||||
|
@@ -209,6 +209,17 @@ def test_add_multi_user_bad(app):
|
||||
r = api_request(app, 'users', method='post', data='[]')
|
||||
assert r.status_code == 400
|
||||
|
||||
|
||||
def test_add_multi_user_invalid(app):
|
||||
app.authenticator.username_pattern = r'w.*'
|
||||
r = api_request(app, 'users', method='post',
|
||||
data=json.dumps({'usernames': ['Willow', 'Andrew', 'Tara']})
|
||||
)
|
||||
app.authenticator.username_pattern = ''
|
||||
assert r.status_code == 400
|
||||
assert r.json()['message'] == 'Invalid usernames: andrew, tara'
|
||||
|
||||
|
||||
def test_add_multi_user(app):
|
||||
db = app.db
|
||||
names = ['a', 'b']
|
||||
|
@@ -169,3 +169,12 @@ def test_username_map(io_loop):
|
||||
assert authorized == 'inara'
|
||||
|
||||
|
||||
def test_validate_names(io_loop):
|
||||
a = auth.PAMAuthenticator()
|
||||
assert a.validate_username('willow')
|
||||
assert a.validate_username('giles')
|
||||
a = auth.PAMAuthenticator(username_pattern='w.*')
|
||||
assert not a.validate_username('xander')
|
||||
assert a.validate_username('willow')
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user