diff --git a/tests/test_normalize.py b/tests/test_normalize.py new file mode 100644 index 0000000..9676365 --- /dev/null +++ b/tests/test_normalize.py @@ -0,0 +1,24 @@ +""" +Test functions for normalizing various kinds of values +""" +from tljh.normalize import generate_system_username + + +def test_generate_username(): + """ + Test generating system usernames from hub usernames + """ + usernames = { + # Very short + 'jupyter-test': 'jupyter-test', + # Very long + 'jupyter-aelie9sohjeequ9iemeipuimuoshahz4aitugiuteeg4ohioh5yuiha6aei7te5z': 'jupyter-aelie9sohjeequ9iem-4b726', + # 26 characters, just below our cutoff for hashing + 'jupyter-abcdefghijklmnopq': 'jupyter-abcdefghijklmnopq', + # 27 characters, just above our cutoff for hashing + 'jupyter-abcdefghijklmnopqr': 'jupyter-abcdefghijklmnopqr-e375e', + + } + for hub_user, system_user in usernames.items(): + assert generate_system_username(hub_user) == system_user + assert len(system_user) <= 32 \ No newline at end of file diff --git a/tljh/jupyterhub_config.py b/tljh/jupyterhub_config.py index 5ad957b..75b52d6 100644 --- a/tljh/jupyterhub_config.py +++ b/tljh/jupyterhub_config.py @@ -7,17 +7,23 @@ import yaml from glob import glob from systemdspawner import SystemdSpawner -from tljh import user, configurer +from tljh import configurer, user from tljh.config import INSTALL_PREFIX, USER_ENV_PREFIX, CONFIG_DIR +from tljh.normalize import generate_system_username -class CustomSpawner(SystemdSpawner): +class UserCreatingSpawner(SystemdSpawner): + """ + SystemdSpawner with user creation on spawn. + + FIXME: Remove this somehow? + """ def start(self): """ Perform system user activities before starting server """ # FIXME: Move this elsewhere? Into the Authenticator? - system_username = 'jupyter-' + self.user.name + system_username = generate_system_username('jupyter-' + self.user.name) user.ensure_user(system_username) user.ensure_user_group(system_username, 'jupyterhub-users') if self.user.admin: @@ -26,8 +32,7 @@ class CustomSpawner(SystemdSpawner): user.remove_user_group(system_username, 'jupyterhub-admins') return super().start() - -c.JupyterHub.spawner_class = CustomSpawner +c.JupyterHub.spawner_class = UserCreatingSpawner # leave users running when the Hub restarts c.JupyterHub.cleanup_servers = False diff --git a/tljh/normalize.py b/tljh/normalize.py new file mode 100644 index 0000000..bba2b41 --- /dev/null +++ b/tljh/normalize.py @@ -0,0 +1,24 @@ +""" +Functions to normalize various inputs +""" +import hashlib + + +def generate_system_username(username): + """ + Generate a posix username from given username. + + If username < 26 char, we just return it. + Else, we hash the username, truncate username at + 26 char, append a '-' and first add 5char of hash. + This makes sure our usernames are always under 32char. + """ + + if len(username) < 26: + return username + + userhash = hashlib.sha256(username.encode('utf-8')).hexdigest() + return '{username_trunc}-{hash}'.format( + username_trunc=username[:26], + hash=userhash[:5] + ) \ No newline at end of file