mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-16 14:33:00 +00:00
Add Authenticator.add_user hook
and .delete_user This hook can be used to trigger events, such as user validation, or creating of system users. Adds a LocalAuthenticator class that implements checking for and rudimentary creation of system users.
This commit is contained in:
@@ -85,10 +85,11 @@ class UserAPIHandler(BaseUserHandler):
|
||||
user.admin = data['admin']
|
||||
self.db.commit()
|
||||
|
||||
# add to whitelist, if a whitelist is in use
|
||||
|
||||
if self.authenticator and self.authenticator.whitelist:
|
||||
self.authenticator.whitelist.add(user.name)
|
||||
try:
|
||||
self.authenticator.add_user(user)
|
||||
except Exception:
|
||||
self.db.delete(user)
|
||||
self.db.commit()
|
||||
|
||||
self.write(json.dumps(self.user_model(user)))
|
||||
self.set_status(201)
|
||||
@@ -104,9 +105,7 @@ class UserAPIHandler(BaseUserHandler):
|
||||
if user.spawner is not None:
|
||||
yield self.stop_single_user(user)
|
||||
|
||||
# remove the user from the whitelist, if there is one
|
||||
if self.authenticator and user.name in self.authenticator.whitelist:
|
||||
self.authenticator.whitelist.remove(user.name)
|
||||
self.authenticator.delete_user(user)
|
||||
|
||||
# remove from the db
|
||||
self.db.delete(user)
|
||||
|
@@ -3,11 +3,14 @@
|
||||
# Copyright (c) IPython Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import pwd
|
||||
from subprocess import check_call, check_output, CalledProcessError
|
||||
|
||||
from tornado import gen
|
||||
import simplepam
|
||||
|
||||
from IPython.config import LoggingConfigurable
|
||||
from IPython.utils.traitlets import Unicode, Set
|
||||
from IPython.utils.traitlets import Bool, Set, Unicode
|
||||
|
||||
from .utils import url_path_join
|
||||
|
||||
@@ -35,6 +38,25 @@ class Authenticator(LoggingConfigurable):
|
||||
and return None on failed authentication.
|
||||
"""
|
||||
|
||||
def add_user(self, user):
|
||||
"""Add a new user
|
||||
|
||||
By default, this just adds the user to the whitelist.
|
||||
|
||||
Subclasses may do more extensive things,
|
||||
such as adding actual unix users.
|
||||
"""
|
||||
if self.whitelist:
|
||||
self.whitelist.add(user.name)
|
||||
|
||||
def delete_user(self, user):
|
||||
"""Triggered when a user is deleted.
|
||||
|
||||
Removes the user from the whitelist.
|
||||
"""
|
||||
if user.name in self.whitelist:
|
||||
self.whitelist.remove(user.name)
|
||||
|
||||
def login_url(self, base_url):
|
||||
"""Override to register a custom login handler"""
|
||||
return url_path_join(base_url, 'login')
|
||||
@@ -50,10 +72,66 @@ class Authenticator(LoggingConfigurable):
|
||||
"""
|
||||
return []
|
||||
|
||||
class LocalAuthenticator(Authenticator):
|
||||
"""Base class for Authenticators that work with local *ix users
|
||||
|
||||
Checks for local users, and can attempt to create them if they exist.
|
||||
"""
|
||||
|
||||
create_system_users = Bool(False, config=True,
|
||||
help="""If a user is added that doesn't exist on the system,
|
||||
should I try to create the system user?
|
||||
"""
|
||||
)
|
||||
|
||||
def add_user(self, user):
|
||||
"""Add a new user
|
||||
|
||||
By default, this just adds the user to the whitelist.
|
||||
|
||||
Subclasses may do more extensive things,
|
||||
such as adding actual unix users.
|
||||
"""
|
||||
if not self.system_user_exists(user):
|
||||
if self.create_system_users:
|
||||
self.add_system_user(user)
|
||||
else:
|
||||
raise KeyError("User %s does not exist." % user.name)
|
||||
|
||||
super(LocalAuthenticator, self).add_user(user)
|
||||
|
||||
def system_user_exists(self, user):
|
||||
"""Check if the user exists on the system"""
|
||||
try:
|
||||
pwd.getpwnam(user.name)
|
||||
except KeyError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def add_system_user(user):
|
||||
"""Create a new *ix user on the system. Works on FreeBSD and Linux, at least."""
|
||||
name = user.name
|
||||
for useradd in (
|
||||
['pw', 'useradd', '-m'],
|
||||
['useradd', '-m'],
|
||||
):
|
||||
try:
|
||||
check_output(['which', useradd[0]])
|
||||
except CalledProcessError:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
else:
|
||||
raise RuntimeError("I don't know how to add users on this system.")
|
||||
|
||||
check_call(useradd + [name])
|
||||
|
||||
class PAMAuthenticator(Authenticator):
|
||||
|
||||
class PAMAuthenticator(LocalAuthenticator):
|
||||
"""Authenticate local *ix users with PAM"""
|
||||
encoding = Unicode('utf8', config=True,
|
||||
help="""The encoding to use for PAM """
|
||||
help="""The encoding to use for PAM"""
|
||||
)
|
||||
service = Unicode('login', config=True,
|
||||
help="""The PAM service to use for authentication."""
|
||||
|
@@ -14,7 +14,7 @@ from IPython.utils.py3compat import unicode_type
|
||||
|
||||
from ..spawner import LocalProcessSpawner
|
||||
from ..app import JupyterHubApp
|
||||
from ..auth import PAMAuthenticator
|
||||
from ..auth import PAMAuthenticator, Authenticator
|
||||
from .. import orm
|
||||
|
||||
def mock_authenticate(username, password, service='login'):
|
||||
@@ -46,6 +46,10 @@ class MockSpawner(LocalProcessSpawner):
|
||||
|
||||
|
||||
class MockPAMAuthenticator(PAMAuthenticator):
|
||||
def system_user_exists(self, user):
|
||||
# skip the add-system-user bit
|
||||
return True
|
||||
|
||||
def authenticate(self, *args, **kwargs):
|
||||
with mock.patch('simplepam.authenticate', mock_authenticate):
|
||||
return super(MockPAMAuthenticator, self).authenticate(*args, **kwargs)
|
||||
|
Reference in New Issue
Block a user