mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-17 06:52:59 +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']
|
user.admin = data['admin']
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
||||||
# add to whitelist, if a whitelist is in use
|
try:
|
||||||
|
self.authenticator.add_user(user)
|
||||||
if self.authenticator and self.authenticator.whitelist:
|
except Exception:
|
||||||
self.authenticator.whitelist.add(user.name)
|
self.db.delete(user)
|
||||||
|
self.db.commit()
|
||||||
|
|
||||||
self.write(json.dumps(self.user_model(user)))
|
self.write(json.dumps(self.user_model(user)))
|
||||||
self.set_status(201)
|
self.set_status(201)
|
||||||
@@ -104,9 +105,7 @@ class UserAPIHandler(BaseUserHandler):
|
|||||||
if user.spawner is not None:
|
if user.spawner is not None:
|
||||||
yield self.stop_single_user(user)
|
yield self.stop_single_user(user)
|
||||||
|
|
||||||
# remove the user from the whitelist, if there is one
|
self.authenticator.delete_user(user)
|
||||||
if self.authenticator and user.name in self.authenticator.whitelist:
|
|
||||||
self.authenticator.whitelist.remove(user.name)
|
|
||||||
|
|
||||||
# remove from the db
|
# remove from the db
|
||||||
self.db.delete(user)
|
self.db.delete(user)
|
||||||
|
@@ -3,11 +3,14 @@
|
|||||||
# Copyright (c) IPython Development Team.
|
# Copyright (c) IPython Development Team.
|
||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
|
|
||||||
|
import pwd
|
||||||
|
from subprocess import check_call, check_output, CalledProcessError
|
||||||
|
|
||||||
from tornado import gen
|
from tornado import gen
|
||||||
import simplepam
|
import simplepam
|
||||||
|
|
||||||
from IPython.config import LoggingConfigurable
|
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
|
from .utils import url_path_join
|
||||||
|
|
||||||
@@ -35,6 +38,25 @@ class Authenticator(LoggingConfigurable):
|
|||||||
and return None on failed authentication.
|
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):
|
def login_url(self, base_url):
|
||||||
"""Override to register a custom login handler"""
|
"""Override to register a custom login handler"""
|
||||||
return url_path_join(base_url, 'login')
|
return url_path_join(base_url, 'login')
|
||||||
@@ -50,10 +72,66 @@ class Authenticator(LoggingConfigurable):
|
|||||||
"""
|
"""
|
||||||
return []
|
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,
|
encoding = Unicode('utf8', config=True,
|
||||||
help="""The encoding to use for PAM """
|
help="""The encoding to use for PAM"""
|
||||||
)
|
)
|
||||||
service = Unicode('login', config=True,
|
service = Unicode('login', config=True,
|
||||||
help="""The PAM service to use for authentication."""
|
help="""The PAM service to use for authentication."""
|
||||||
|
@@ -14,7 +14,7 @@ from IPython.utils.py3compat import unicode_type
|
|||||||
|
|
||||||
from ..spawner import LocalProcessSpawner
|
from ..spawner import LocalProcessSpawner
|
||||||
from ..app import JupyterHubApp
|
from ..app import JupyterHubApp
|
||||||
from ..auth import PAMAuthenticator
|
from ..auth import PAMAuthenticator, Authenticator
|
||||||
from .. import orm
|
from .. import orm
|
||||||
|
|
||||||
def mock_authenticate(username, password, service='login'):
|
def mock_authenticate(username, password, service='login'):
|
||||||
@@ -46,6 +46,10 @@ class MockSpawner(LocalProcessSpawner):
|
|||||||
|
|
||||||
|
|
||||||
class MockPAMAuthenticator(PAMAuthenticator):
|
class MockPAMAuthenticator(PAMAuthenticator):
|
||||||
|
def system_user_exists(self, user):
|
||||||
|
# skip the add-system-user bit
|
||||||
|
return True
|
||||||
|
|
||||||
def authenticate(self, *args, **kwargs):
|
def authenticate(self, *args, **kwargs):
|
||||||
with mock.patch('simplepam.authenticate', mock_authenticate):
|
with mock.patch('simplepam.authenticate', mock_authenticate):
|
||||||
return super(MockPAMAuthenticator, self).authenticate(*args, **kwargs)
|
return super(MockPAMAuthenticator, self).authenticate(*args, **kwargs)
|
||||||
|
Reference in New Issue
Block a user