Skip to content

Commit

Permalink
Merge pull request #3008 from Carreau/autopawd
Browse files Browse the repository at this point in the history
When login-in via token, let a chance for user to set the password
  • Loading branch information
takluyver authored Nov 15, 2017
2 parents c097387 + a40ab05 commit 74fbc5b
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 10 deletions.
42 changes: 35 additions & 7 deletions docs/source/public_server.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,28 @@ using the following command::

$ jupyter notebook --generate-config

.. _hashed-pw:

Preparing a hashed password
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Automatic Password setup
~~~~~~~~~~~~~~~~~~~~~~~~

As of notebook 5.3, the first time you log-in using a token, the notebook server
should give you the opportunity to setup a password from the user interface.

You will be presented with a form asking for the current _token_, as well as
your _new_ _password_ ; enter both and click on ``Login and setup new password``.

Next time you need to log in you'll be able to use the new password instead of
the login token, otherwise follow the procedure to set a password from the
command line.

The ability to change the password at first login time may be disabled by
integrations by setting the ``--NotebookApp.allow_password_change=False``


As of notebook version 5.0, you can enter and store a password for your
notebook server with a single command.
:command:`jupyter notebook password` will prompt you for your password
and record the hashed password in your :file:`jupyter_notebook_config.json`.
Starting at notebook version 5.0, you can enter and store a password for your
notebook server with a single command. :command:`jupyter notebook password` will
prompt you for your password and record the hashed password in your
:file:`jupyter_notebook_config.json`.

.. code-block:: bash
Expand All @@ -80,6 +93,15 @@ and record the hashed password in your :file:`jupyter_notebook_config.json`.
Verify password: ****
[NotebookPasswordApp] Wrote hashed password to /Users/you/.jupyter/jupyter_notebook_config.json
This can be used to reset a lost password; or if you believe your credentials
have been leaked and desire to change your password. Changing your password will
invalidate all logged-in sessions after a server restart.

.. _hashed-pw:

Preparing a hashed password
~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can prepare a hashed password manually, using the function
:func:`notebook.auth.security.passwd`:

Expand Down Expand Up @@ -109,6 +131,12 @@ directory, ``~/.jupyter``, e.g.::

c.NotebookApp.password = u'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'

Automatic password setup will store the hash in ``jupyter_notebook_config.json``
while this method store in in ``jupyter_notebook_config.py``. The ``.json``
configuration options take precedence over the ``.py`` one, thus the manual
password may not take effect if the Json file as a password set.


Using SSL for encrypted communication
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When using a password, it is a good idea to also use SSL with a web
Expand Down
17 changes: 14 additions & 3 deletions notebook/auth/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# Distributed under the terms of the Modified BSD License.

import re
import os

try:
from urllib.parse import urlparse # Py 3
Expand All @@ -13,7 +14,7 @@

from tornado.escape import url_escape

from ..auth.security import passwd_check
from .security import passwd_check, set_password

from ..base.handlers import IPythonHandler

Expand Down Expand Up @@ -72,16 +73,26 @@ def passwd_check(self, a, b):

def post(self):
typed_password = self.get_argument('password', default=u'')
new_password = self.get_argument('new_password', default=u'')



if self.get_login_available(self.settings):
if self.passwd_check(self.hashed_password, typed_password):
if self.passwd_check(self.hashed_password, typed_password) and not new_password:
self.set_login_cookie(self, uuid.uuid4().hex)
elif self.token and self.token == typed_password:
self.set_login_cookie(self, uuid.uuid4().hex)
if self.new_password and self.settings.get('allow_password_change'):
config_dir = self.settings.get('config_dir')
config_file = os.path.join(config_dir, 'jupyter_notebook_config.json')
set_password(new_password, config_file=config_file)
self.log.info("Wrote hashed password to %s" % config_file)
else:
self.set_status(401)
self._render(message={'error': 'Invalid password'})
self._render(message={'error': 'Invalid credentials'})
return


next_url = self.get_argument('next', default=self.base_url)
self._redirect_safe(next_url)

Expand Down
1 change: 1 addition & 0 deletions notebook/base/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ def template_namespace(self):
default_url=self.default_url,
ws_url=self.ws_url,
logged_in=self.logged_in,
allow_password_change=self.settings.get('allow_password_change'),
login_available=self.login_available,
token_available=bool(self.token or self.one_time_token),
static_url=self.static_url,
Expand Down
13 changes: 13 additions & 0 deletions notebook/notebookapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ def init_settings(self, jupyter_app, kernel_manager, contents_manager,
mathjax_config=jupyter_app.mathjax_config,
config=jupyter_app.config,
config_dir=jupyter_app.config_dir,
allow_password_change=jupyter_app.allow_password_change,
server_root_dir=root_dir,
jinja2_env=env,
terminals_available=False, # Set later if terminals are available
Expand Down Expand Up @@ -756,6 +757,18 @@ def _token_changed(self, change):
"""
)

allow_password_change = Bool(True, config=True,
help="""Allow password to be changed at login for the notebook server.
While loggin in with a token, the notebook server UI will give the opportunity to
the user to enter a new password at the same time that will replace
the token login mechanism.
This can be set to false to prevent changing password from the UI/API.
"""
)


disable_check_xsrf = Bool(False, config=True,
help="""Disable cross-site-request-forgery protection
Expand Down
18 changes: 18 additions & 0 deletions notebook/templates/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,24 @@ <h3>
<p>
Cookies are required for authenticated access to notebooks.
</p>
{% if allow_password_change %}
<h3>{% trans %}Setup a Password{% endtrans %}</h3>
<p> You can also setup a password by entering your token and a new password
on the fields below:</p>
<form action="{{base_url}}login?next={{next}}" method="post" class="">
{{ xsrf_form_html() | safe }}
<div class="form-group">
<input type="password" name="password" id="password_input" class="form-control" placeholder="Token">
</div>
<div class="form-group">
<input type="password" name="new_password" id="new_password_input"
class="form-control" placeholder="New password" required>
</div>
<div class="form-group">
<button type="submit" id="login_submit">{% trans %}Log in and set new password{% endtrans %}</button>
</div>
</form>
{% endif %}

</div>
{% endblock token_message %}
Expand Down

0 comments on commit 74fbc5b

Please sign in to comment.