diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index 3d8311f3..6d0d2b9b 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -1383,3 +1383,52 @@ 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_dir_template = Unicode( + '/tmp/{username}', + config=True, + help=""" + Template to expand to set the user home. + {username} is expanded to the jupyterhub username. + """ + ) + + 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_dir + def preexec(): + try: + os.makedirs(home, 0o755, exist_ok=True) + os.chdir(home) + except Exception as 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_dir + env['SHELL'] = '/bin/bash' + return env + + def move_certs(self, paths): + """No-op for installing certs""" + return paths + diff --git a/jupyterhub/tests/mocking.py b/jupyterhub/tests/mocking.py index b8749ba3..44205427 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: 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 = [ 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