Skip to content

Commit

Permalink
Merge e7868e6 into 94ac73c
Browse files Browse the repository at this point in the history
  • Loading branch information
meeseeksmachine committed Dec 13, 2018
2 parents 94ac73c + e7868e6 commit 4d8af43
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 49 deletions.
6 changes: 0 additions & 6 deletions notebook/auth/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,17 +204,11 @@ def get_user_token(cls, handler):
return
# check login token from URL argument or Authorization header
user_token = cls.get_token(handler)
one_time_token = handler.one_time_token
authenticated = False
if user_token == token:
# token-authenticated, set the login cookie
handler.log.debug("Accepting token-authenticated connection from %s", handler.request.remote_ip)
authenticated = True
elif one_time_token and user_token == one_time_token:
# one-time-token-authenticated, only allow this token once
handler.settings.pop('one_time_token', None)
handler.log.info("Accepting one-time-token-authenticated connection from %s", handler.request.remote_ip)
authenticated = True

if authenticated:
return uuid.uuid4().hex
Expand Down
7 changes: 1 addition & 6 deletions notebook/base/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,6 @@ def token(self):
"""Return the login token for this application, if any."""
return self.settings.get('token', None)

@property
def one_time_token(self):
"""Return the one-time-use token for this application, if any."""
return self.settings.get('one_time_token', None)

@property
def login_available(self):
"""May a user proceed to log in?
Expand Down Expand Up @@ -475,7 +470,7 @@ def template_namespace(self):
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),
token_available=bool(self.token),
static_url=self.static_url,
sys_info=json_sys_info(),
contents_js_source=self.contents_js_source,
Expand Down
110 changes: 76 additions & 34 deletions notebook/notebookapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import signal
import socket
import sys
import tempfile
import threading
import time
import warnings
Expand Down Expand Up @@ -107,7 +108,7 @@
from notebook._sysinfo import get_sys_info

from ._tz import utcnow, utcfromtimestamp
from .utils import url_path_join, check_pid, url_escape
from .utils import url_path_join, check_pid, url_escape, urljoin, pathname2url

#-----------------------------------------------------------------------------
# Module globals
Expand Down Expand Up @@ -754,12 +755,6 @@ def _write_cookie_secret_file(self, secret):
""")
).tag(config=True)

one_time_token = Unicode(
help=_("""One-time token used for opening a browser.
Once used, this token cannot be used again.
""")
)

_token_generated = True

@default('token')
Expand Down Expand Up @@ -1178,6 +1173,13 @@ def _update_mathjax_config(self, change):
def _default_info_file(self):
info_file = "nbserver-%s.json" % os.getpid()
return os.path.join(self.runtime_dir, info_file)

browser_open_file = Unicode()

@default('browser_open_file')
def _default_browser_open_file(self):
basename = "nbserver-%s-open.html" % os.getpid()
return os.path.join(self.runtime_dir, basename)

pylab = Unicode('disabled', config=True,
help=_("""
Expand Down Expand Up @@ -1357,9 +1359,6 @@ def init_webapp(self):
self.tornado_settings['cookie_options'] = self.cookie_options
self.tornado_settings['get_secure_cookie_kwargs'] = self.get_secure_cookie_kwargs
self.tornado_settings['token'] = self.token
if (self.open_browser or self.file_to_run) and not self.password:
self.one_time_token = binascii.hexlify(os.urandom(24)).decode('ascii')
self.tornado_settings['one_time_token'] = self.one_time_token

# ensure default_url starts with base_url
if not self.default_url.startswith(self.base_url):
Expand Down Expand Up @@ -1689,6 +1688,67 @@ def remove_server_info_file(self):
if e.errno != errno.ENOENT:
raise

def write_browser_open_file(self):
"""Write an nbserver-<pid>-open.html file
This can be used to open the notebook in a browser
"""
# default_url contains base_url, but so does connection_url
open_url = self.default_url[len(self.base_url):]

with open(self.browser_open_file, 'w', encoding='utf-8') as f:
self._write_browser_open_file(open_url, f)

def _write_browser_open_file(self, url, fh):
if self.token:
url = url_concat(url, {'token': self.token})
url = url_path_join(self.connection_url, url)

jinja2_env = self.web_app.settings['jinja2_env']
template = jinja2_env.get_template('browser-open.html')
fh.write(template.render(open_url=url))

def remove_browser_open_file(self):
"""Remove the nbserver-<pid>-open.html file created for this server.
Ignores the error raised when the file has already been removed.
"""
try:
os.unlink(self.browser_open_file)
except OSError as e:
if e.errno != errno.ENOENT:
raise

def launch_browser(self):
try:
browser = webbrowser.get(self.browser or None)
except webbrowser.Error as e:
self.log.warning(_('No web browser found: %s.') % e)
browser = None

if not browser:
return

if self.file_to_run:
if not os.path.exists(self.file_to_run):
self.log.critical(_("%s does not exist") % self.file_to_run)
self.exit(1)

relpath = os.path.relpath(self.file_to_run, self.notebook_dir)
uri = url_escape(url_path_join('notebooks', *relpath.split(os.sep)))

# Write a temporary file to open in the browser
fd, open_file = tempfile.mkstemp(suffix='.html')
with open(fd, 'w', encoding='utf-8') as fh:
self._write_browser_open_file(uri, fh)
else:
open_file = self.browser_open_file

b = lambda: browser.open(
urljoin('file:', pathname2url(open_file)),
new=self.webbrowser_open_new)
threading.Thread(target=b).start()

def start(self):
""" Start the Notebook server app, after initialization
Expand Down Expand Up @@ -1718,38 +1778,19 @@ def start(self):
"resources section at https://jupyter.org/community.html."))

self.write_server_info_file()
self.write_browser_open_file()

if self.open_browser or self.file_to_run:
try:
browser = webbrowser.get(self.browser or None)
except webbrowser.Error as e:
self.log.warning(_('No web browser found: %s.') % e)
browser = None

if self.file_to_run:
if not os.path.exists(self.file_to_run):
self.log.critical(_("%s does not exist") % self.file_to_run)
self.exit(1)

relpath = os.path.relpath(self.file_to_run, self.notebook_dir)
uri = url_escape(url_path_join('notebooks', *relpath.split(os.sep)))
else:
# default_url contains base_url, but so does connection_url
uri = self.default_url[len(self.base_url):]
if self.one_time_token:
uri = url_concat(uri, {'token': self.one_time_token})
if browser:
b = lambda : browser.open(url_path_join(self.connection_url, uri),
new=self.webbrowser_open_new)
threading.Thread(target=b).start()
self.launch_browser()

if self.token and self._token_generated:
# log full URL with generated token, so there's a copy/pasteable link
# with auth info.
self.log.critical('\n'.join([
'\n',
'Copy/paste this URL into your browser when you connect for the first time,',
'to login with a token:',
'To access the notebook, open this file in a browser:',
' %s' % urljoin('file:', pathname2url(self.browser_open_file)),
'Or copy and paste one of these URLs:',
' %s' % self.display_url,
]))

Expand All @@ -1765,6 +1806,7 @@ def start(self):
info(_("Interrupted..."))
finally:
self.remove_server_info_file()
self.remove_browser_open_file()
self.cleanup_kernels()

def stop(self):
Expand Down
18 changes: 18 additions & 0 deletions notebook/templates/browser-open.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{# This template is not served, but written as a file to open in the browser,
passing the token without putting it in a command-line argument. #}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="1;url={{ open_url }}" />
<title>Opening Jupyter Notebook</title>
</head>
<body>

<p>
This page should redirect you to Jupyter Notebook. If it doesn't,
<a href="{{ open_url }}">click here to go to Jupyter</a>.
</p>

</body>
</html>
7 changes: 4 additions & 3 deletions notebook/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
from distutils.version import LooseVersion

try:
from urllib.parse import quote, unquote, urlparse
from urllib.parse import quote, unquote, urlparse, urljoin
from urllib.request import pathname2url
except ImportError:
from urllib import quote, unquote
from urlparse import urlparse
from urllib import quote, unquote, pathname2url
from urlparse import urlparse, urljoin

from ipython_genutils import py3compat

Expand Down

0 comments on commit 4d8af43

Please sign in to comment.