mirror of
https://github.com/jupyter/notebook.git
synced 2025-01-06 11:35:24 +08:00
Launch the browser with a redirect file
This avoids putting the authentication token into a command-line argument to launch the browser, where it's visible to other users. Filesystem permissions should ensure that only the user who started the notebook can use this route to authenticate. Thanks to Dr Owain Kenway for suggesting this technique.
This commit is contained in:
parent
f759e4d3be
commit
270c0f96d7
@ -26,6 +26,7 @@ import select
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
import warnings
|
||||
@ -107,7 +108,7 @@ from jupyter_core.paths import jupyter_runtime_dir, jupyter_path
|
||||
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
|
||||
@ -1184,6 +1185,13 @@ class NotebookApp(JupyterApp):
|
||||
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=_("""
|
||||
@ -1697,6 +1705,67 @@ class NotebookApp(JupyterApp):
|
||||
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.one_time_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
|
||||
|
||||
@ -1726,30 +1795,10 @@ class NotebookApp(JupyterApp):
|
||||
"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
|
||||
@ -1773,6 +1822,7 @@ class NotebookApp(JupyterApp):
|
||||
info(_("Interrupted..."))
|
||||
finally:
|
||||
self.remove_server_info_file()
|
||||
self.remove_browser_open_file()
|
||||
self.cleanup_kernels()
|
||||
|
||||
def stop(self):
|
||||
|
18
notebook/templates/browser-open.html
Normal file
18
notebook/templates/browser-open.html
Normal file
@ -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>
|
@ -13,10 +13,11 @@ import sys
|
||||
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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user