From 80e241c86f6fa06f540059377dc94eb3ea18a525 Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 5 Nov 2018 10:55:40 +0100 Subject: [PATCH 1/5] move simplespawner into jupyterhub.spawner --- jupyterhub/spawner.py | 41 +++++++++++++++++++++++++++++++++++++ spawners/__init__.py | 1 - spawners/simplespawner.py | 43 --------------------------------------- 3 files changed, 41 insertions(+), 44 deletions(-) delete mode 100644 spawners/__init__.py delete mode 100644 spawners/simplespawner.py diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index 3d8311f3..366fbbce 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -1383,3 +1383,44 @@ class LocalProcessSpawner(Spawner): if status is None: # it all failed, zombie process self.log.warning("Process %i never died", self.pid) + + +class SimpleLocalProcessSpawner(LocalProcessSpawner): + """ + A version of LocalProcessSpawner that doesn't require users to exist on + the system beforehand. + + Only use this for testing. + + Note: DO NOT USE THIS FOR PRODUCTION USE CASES! It is very insecure, and + provides absolutely no isolation between different users! + """ + + home_path_template = Unicode( + '/tmp/{username}', + config=True, + help='Template to expand to set the user home. {username} is expanded' + ) + + @property + def home_path(self): + return self.home_path_template.format( + username=self.user.name, + ) + + def make_preexec_fn(self, name): + home = self.home_path + def preexec(): + try: + os.makedirs(home, 0o755, exist_ok=True) + os.chdir(home) + except Exception as e: + + print(e) + return preexec + + def user_env(self, env): + env['USER'] = self.user.name + env['HOME'] = self.home_path + env['SHELL'] = '/bin/bash' + diff --git a/spawners/__init__.py b/spawners/__init__.py deleted file mode 100644 index ef919810..00000000 --- a/spawners/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .simplespawner import SimpleSpawner \ No newline at end of file diff --git a/spawners/simplespawner.py b/spawners/simplespawner.py deleted file mode 100644 index 8df323c9..00000000 --- a/spawners/simplespawner.py +++ /dev/null @@ -1,43 +0,0 @@ -import os -from traitlets import Unicode - -from jupyterhub.spawner import LocalProcessSpawner - - -class SimpleLocalProcessSpawner(LocalProcessSpawner): - """ - A version of LocalProcessSpawner that doesn't require users to exist on - the system beforehand. - - Note: DO NOT USE THIS FOR PRODUCTION USE CASES! It is very insecure, and - provides absolutely no isolation between different users! - """ - - home_path_template = Unicode( - '/tmp/{userid}', - config=True, - help='Template to expand to set the user home. {userid} and {username} are expanded' - ) - - @property - def home_path(self): - return self.home_path_template.format( - userid=self.user.id, - username=self.user.name - ) - - def make_preexec_fn(self, name): - home = self.home_path - def preexec(): - try: - os.makedirs(home, 0o755, exist_ok=True) - os.chdir(home) - except e: - print(e) - return preexec - - def user_env(self, env): - env['USER'] = self.user.name - env['HOME'] = self.home_path - env['SHELL'] = '/bin/bash' - return env \ No newline at end of file From 52c468d89c53241f4d9b9de9c46eb134e52753ca Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 5 Nov 2018 10:57:02 +0100 Subject: [PATCH 2/5] make home_dir a traitlet so the property is only evaluated once and overrideable via hooks --- jupyterhub/spawner.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index 366fbbce..b221a71a 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -1396,31 +1396,34 @@ class SimpleLocalProcessSpawner(LocalProcessSpawner): provides absolutely no isolation between different users! """ - home_path_template = Unicode( + home_dir_template = Unicode( '/tmp/{username}', config=True, - help='Template to expand to set the user home. {username} is expanded' + help=""" + Template to expand to set the user home. + {username} is expanded to the jupyterhub username. + """ ) - @property - def home_path(self): - return self.home_path_template.format( + home_dir = Unicode(help="The home directory for the user") + @default('home_dir') + def _default_home_dir(self): + return self.home_dir_template.format( username=self.user.name, ) def make_preexec_fn(self, name): - home = self.home_path + home = self.home_dir def preexec(): try: os.makedirs(home, 0o755, exist_ok=True) os.chdir(home) except Exception as e: - - print(e) + self.log.exception("Error in preexec for %s", name) return preexec def user_env(self, env): env['USER'] = self.user.name - env['HOME'] = self.home_path + env['HOME'] = self.home_dir env['SHELL'] = '/bin/bash' From 575af23e236c3904b151a4aa0005dac0ce4ccf28 Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 5 Nov 2018 10:57:10 +0100 Subject: [PATCH 3/5] register simplespawner in setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index b085bce5..5b14fc37 100755 --- a/setup.py +++ b/setup.py @@ -119,6 +119,7 @@ setup_args = dict( 'jupyterhub.spawners': [ 'default = jupyterhub.spawner:LocalProcessSpawner', 'localprocess = jupyterhub.spawner:LocalProcessSpawner', + 'simple = jupyterhub.spawner:SimpleLocalProcessSpawner', ], }, classifiers = [ From 4fb158933ed4e329f8e803aedc66ad4ebc502b19 Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 5 Nov 2018 11:01:06 +0100 Subject: [PATCH 4/5] no-op move_certs in simplespawner --- jupyterhub/spawner.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index b221a71a..6d0d2b9b 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -1426,4 +1426,9 @@ class SimpleLocalProcessSpawner(LocalProcessSpawner): env['USER'] = self.user.name env['HOME'] = self.home_dir env['SHELL'] = '/bin/bash' + return env + + def move_certs(self, paths): + """No-op for installing certs""" + return paths From e512847652bef695310af13c268d3a8b0e8bb7f2 Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 5 Nov 2018 11:01:21 +0100 Subject: [PATCH 5/5] use simplespawner as base for testing --- jupyterhub/tests/mocking.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/jupyterhub/tests/mocking.py b/jupyterhub/tests/mocking.py index 2c553e34..e6540e4d 100644 --- a/jupyterhub/tests/mocking.py +++ b/jupyterhub/tests/mocking.py @@ -46,7 +46,7 @@ from ..app import JupyterHub from ..auth import PAMAuthenticator from .. import orm from ..objects import Server -from ..spawner import LocalProcessSpawner +from ..spawner import LocalProcessSpawner, SimpleLocalProcessSpawner from ..singleuser import SingleUserNotebookApp from ..utils import random_port, url_path_join from .utils import async_requests, ssl_setup @@ -73,20 +73,14 @@ def mock_open_session(username, service, encoding): pass -class MockSpawner(LocalProcessSpawner): +class MockSpawner(SimpleLocalProcessSpawner): """Base mock spawner - disables user-switching that we need root permissions to do - spawns `jupyterhub.tests.mocksu` instead of a full single-user server """ - def make_preexec_fn(self, *a, **kw): - # skip the setuid stuff - return - - def _set_user_changed(self, name, old, new): - pass - def user_env(self, env): + env = super().user_env(env) if self.handler: env['HANDLER_ARGS'] = self.handler.request.query return env @@ -95,10 +89,6 @@ class MockSpawner(LocalProcessSpawner): def _cmd_default(self): return [sys.executable, '-m', 'jupyterhub.tests.mocksu'] - def move_certs(self, paths): - """Return the paths unmodified""" - return paths - use_this_api_token = None def start(self): if self.use_this_api_token: