diff --git a/README.md b/README.md index de733b1..f7b2676 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # JupyterHub First Use Authenticator # -A [JupyterHub](https://jupyterhub.readthedocs.io) authenticator that helps new users set their password on their first login to JupyterHub. +A [JupyterHub](https://jupyterhub.readthedocs.io) authenticator that helps new users set their password on their first login to JupyterHub. **Are you running a workshop from a single physical location, such as a university seminar or a user group?** -JupyterHub First Use Authenticator can simplify the user set up for you. It's very useful when using transient +JupyterHub First Use Authenticator can simplify the user set up for you. It's very useful when using transient JupyterHub instances in a single physical location. It allows multiple users to log in, but you do not have install a pre-existing authentication setup. With this authenticator, users can just pick a username and password and get to work! ## Installation ## @@ -23,10 +23,18 @@ c.JupyterHub.authenticator_class = 'firstuseauthenticator.FirstUseAuthenticator' ## Configuration ## -It works out of the box as advertised. There is one configuration parameter, `dbm_path`, which you can tweak. - ### FirstUseAuthenticator.dbm_path ### Path to the [dbm](https://docs.python.org/3.5/library/dbm.html) file, or a UNIX database file such as `passwords.dbm`, used to store usernames and passwords. The dbm file should be put where regular users do not have read/write access to it. This authenticator's default setting for the path to the `passwords.dbm` is the current directory from which JupyterHub is spawned. + +### FirstUseAuthenticator.create_users ### + +Create users if they do not exist already. + +When set to False, users would have to be explicitly created before +they can log in. Users can be created via the admin panel or by setting +whitelist / admin list. + +Defaults to True. diff --git a/firstuseauthenticator/firstuseauthenticator.py b/firstuseauthenticator/firstuseauthenticator.py index 0497980..91c2572 100644 --- a/firstuseauthenticator/firstuseauthenticator.py +++ b/firstuseauthenticator/firstuseauthenticator.py @@ -7,9 +7,10 @@ locally in a dbm file, and checked next time they log in. """ import dbm from jupyterhub.auth import Authenticator +from jupyterhub.orm import User from tornado import gen -from traitlets.traitlets import Unicode +from traitlets.traitlets import Unicode, Bool import bcrypt @@ -26,16 +27,41 @@ class FirstUseAuthenticator(Authenticator): """ ) + create_users = Bool( + True, + config=True, + help=""" + Create users if they do not exist already. + + When set to false, users would have to be explicitly created before + they can log in. Users can be created via the admin panel or by setting + whitelist / admin list. + """ + ) + + def _user_exists(self, username): + """ + Return true if given user already exists. + + Note: Depends on internal details of JupyterHub that might change + across versions. Tested with v0.9 + """ + return self.db.query(User).filter_by(name=username).first() is not None + @gen.coroutine def authenticate(self, handler, data): - # Move everything to bytes - username = data['username'].encode('utf-8') - password = data['password'].encode('utf-8') + username = data['username'] + + if not self.create_users: + if not self._user_exists(username): + return None + + password = data['password'] with dbm.open(self.dbm_path, 'c', 0o600) as db: - stored_pw = db.get(username, None) + stored_pw = db.get(username.encode(), None) if stored_pw is not None: - if bcrypt.hashpw(password, stored_pw) != stored_pw: + if bcrypt.hashpw(password.encode(), stored_pw) != stored_pw: return None else: - db[username] = bcrypt.hashpw(password, bcrypt.gensalt()) - return data['username'] + db[username] = bcrypt.hashpw(password.encode(), bcrypt.gensalt()) + return username