diff --git a/jupyterhub/auth.py b/jupyterhub/auth.py index 7daa97bb..67ab8c51 100644 --- a/jupyterhub/auth.py +++ b/jupyterhub/auth.py @@ -3,6 +3,7 @@ # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. +from grp import getgrnam import pwd from subprocess import check_call, check_output, CalledProcessError @@ -39,7 +40,14 @@ class Authenticator(LoggingConfigurable): It must return the username on successful authentication, and return None on failed authentication. """ - + + def check_whitelist(self, user): + """ + Return True if the whitelist is empty or user is in the whitelist. + """ + # Parens aren't necessary here, but they make this easier to parse. + return (not self.whitelist) or (user in self.whitelist) + def add_user(self, user): """Add a new user @@ -56,8 +64,7 @@ class Authenticator(LoggingConfigurable): Removes the user from the whitelist. """ - if user.name in self.whitelist: - self.whitelist.remove(user.name) + self.whitelist.discard(user.name) def login_url(self, base_url): """Override to register a custom login handler""" @@ -87,7 +94,37 @@ class LocalAuthenticator(Authenticator): should I try to create the system user? """ ) - + + group_whitelist = Set( + config=True, + help="Automatically whitelist anyone in this group.", + ) + + def _group_whitelist_changed(self, name, old, new): + if self.whitelist: + self.log.warn( + "Ignoring username whitelist because group whitelist supplied!" + ) + + def check_whitelist(self, username): + if self.group_whitelist: + return self.check_group_whitelist(username) + else: + return super().check_whitelist(username) + + def check_group_whitelist(self, username): + if not self.group_whitelist: + return False + for group in self.group_whitelist: + try: + group = getgrnam(self.group_whitelist) + except KeyError: + self.log.error('No such group: [%s]' % self.group_whitelist) + continue + if username in group.gr_mem: + return True + return False + @gen.coroutine def add_user(self, user): """Add a new user @@ -152,7 +189,7 @@ class PAMAuthenticator(LocalAuthenticator): Return None otherwise. """ username = data['username'] - if self.whitelist and username not in self.whitelist: + if not self.check_whitelist(username): return # simplepam wants bytes, not unicode # see simplepam#3