mirror of
https://github.com/jupyter/notebook.git
synced 2024-12-27 04:20:22 +08:00
Added authentication configuration for the notebook app.
This commit is contained in:
parent
a84f35373f
commit
d81990c941
@ -3,10 +3,11 @@
|
||||
Authors:
|
||||
|
||||
* Brian Granger
|
||||
* Phil Elson
|
||||
"""
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (C) 2011 The IPython Development Team
|
||||
# Copyright (C) 2014 The IPython Development Team
|
||||
#
|
||||
# Distributed under the terms of the BSD License. The full license is in
|
||||
# the file COPYING, distributed as part of this software.
|
||||
@ -19,7 +20,9 @@ Authors:
|
||||
import uuid
|
||||
|
||||
from tornado.escape import url_escape
|
||||
from tornado import web
|
||||
|
||||
from IPython.config.configurable import Configurable
|
||||
from IPython.lib.security import passwd_check
|
||||
|
||||
from ..base.handlers import IPythonHandler
|
||||
@ -29,7 +32,10 @@ from ..base.handlers import IPythonHandler
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
class LoginHandler(IPythonHandler):
|
||||
|
||||
""" The basic IPythonWebApplication login handler which authenticates with a
|
||||
hashed password from the configuration.
|
||||
|
||||
"""
|
||||
def _render(self, message=None):
|
||||
self.write(self.render_template('login.html',
|
||||
next=url_escape(self.get_argument('next', default=self.base_url)),
|
||||
@ -43,20 +49,38 @@ class LoginHandler(IPythonHandler):
|
||||
self._render()
|
||||
|
||||
def post(self):
|
||||
pwd = self.get_argument('password', default=u'')
|
||||
if self.login_available:
|
||||
if passwd_check(self.password, pwd):
|
||||
hashed_password = self.password_from_configuration(self.application)
|
||||
typed_password = self.get_argument('password', default=u'')
|
||||
if self.login_available(self.application):
|
||||
if passwd_check(hashed_password, typed_password):
|
||||
self.set_secure_cookie(self.cookie_name, str(uuid.uuid4()))
|
||||
else:
|
||||
self._render(message={'error': 'Invalid password'})
|
||||
return
|
||||
|
||||
self.redirect(self.get_argument('next', default=self.base_url))
|
||||
|
||||
@classmethod
|
||||
def validate_notebook_app_security(cls, notebook_app, ssl_options=None):
|
||||
if not notebook_app.ip:
|
||||
warning = "WARNING: The notebook server is listening on all IP addresses"
|
||||
if ssl_options is None:
|
||||
notebook_app.log.critical(warning + " and not using encryption. This "
|
||||
"is not recommended.")
|
||||
if not self.password_from_configuration(notebook_app):
|
||||
notebook_app.log.critical(warning + " and not using authentication. "
|
||||
"This is highly insecure and not recommended.")
|
||||
|
||||
@staticmethod
|
||||
def password_from_configuration(webapp):
|
||||
""" Return the hashed password from the given NotebookWebApplication's configuration.
|
||||
|
||||
If there is no configured password, None will be returned.
|
||||
|
||||
"""
|
||||
return webapp.settings['config']['NotebookApp'].get('password', None)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# URL to handler mappings
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
default_handlers = [(r"/login", LoginHandler)]
|
||||
@classmethod
|
||||
def login_available(cls, webapp):
|
||||
"""Whether this LoginHandler is needed - and therefore whether the login page should be displayed."""
|
||||
return bool(cls.password_from_configuration(webapp))
|
||||
|
@ -86,11 +86,6 @@ class AuthenticatedHandler(web.RequestHandler):
|
||||
))
|
||||
return self.settings.get('cookie_name', default_cookie_name)
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
"""our password"""
|
||||
return self.settings.get('password', '')
|
||||
|
||||
@property
|
||||
def logged_in(self):
|
||||
"""Is a user currently logged in?
|
||||
@ -99,6 +94,11 @@ class AuthenticatedHandler(web.RequestHandler):
|
||||
user = self.get_current_user()
|
||||
return (user and not user == 'anonymous')
|
||||
|
||||
@property
|
||||
def _login_handler(self):
|
||||
"""Return the login handler for this application."""
|
||||
return self.settings['login_handler_class']
|
||||
|
||||
@property
|
||||
def login_available(self):
|
||||
"""May a user proceed to log in?
|
||||
@ -107,7 +107,7 @@ class AuthenticatedHandler(web.RequestHandler):
|
||||
whether the user is already logged in or not.
|
||||
|
||||
"""
|
||||
return bool(self.settings.get('password', ''))
|
||||
return bool(self._login_handler.login_available(self.application))
|
||||
|
||||
|
||||
class IPythonHandler(AuthenticatedHandler):
|
||||
|
@ -182,8 +182,9 @@ class NotebookWebApplication(web.Application):
|
||||
# authentication
|
||||
cookie_secret=ipython_app.cookie_secret,
|
||||
login_url=url_path_join(base_url,'/login'),
|
||||
login_handler_class=ipython_app.login_handler_class,
|
||||
password=ipython_app.password,
|
||||
|
||||
|
||||
# managers
|
||||
kernel_manager=kernel_manager,
|
||||
contents_manager=contents_manager,
|
||||
@ -193,7 +194,7 @@ class NotebookWebApplication(web.Application):
|
||||
config_manager=config_manager,
|
||||
|
||||
# IPython stuff
|
||||
nbextensions_path = ipython_app.nbextensions_path,
|
||||
nbextensions_path=ipython_app.nbextensions_path,
|
||||
websocket_url=ipython_app.websocket_url,
|
||||
mathjax_url=ipython_app.mathjax_url,
|
||||
config=ipython_app.config,
|
||||
@ -211,7 +212,7 @@ class NotebookWebApplication(web.Application):
|
||||
# Order matters. The first handler to match the URL will handle the request.
|
||||
handlers = []
|
||||
handlers.extend(load_handlers('tree.handlers'))
|
||||
handlers.extend(load_handlers('auth.login'))
|
||||
handlers.extend([(r"/login", settings['login_handler_class'])])
|
||||
handlers.extend(load_handlers('auth.logout'))
|
||||
handlers.extend(load_handlers('files.handlers'))
|
||||
handlers.extend(load_handlers('notebook.handlers'))
|
||||
@ -501,7 +502,6 @@ class NotebookApp(BaseIPythonApplication):
|
||||
|
||||
jinja_environment_options = Dict(config=True,
|
||||
help="Supply extra arguments that will be passed to Jinja environment.")
|
||||
|
||||
|
||||
enable_mathjax = Bool(True, config=True,
|
||||
help="""Whether to enable MathJax for typesetting math/TeX
|
||||
@ -639,7 +639,6 @@ class NotebookApp(BaseIPythonApplication):
|
||||
def _kernel_spec_manager_default(self):
|
||||
return KernelSpecManager(ipython_dir=self.ipython_dir)
|
||||
|
||||
|
||||
kernel_spec_manager_class = DottedObjectName('IPython.kernel.kernelspec.KernelSpecManager',
|
||||
config=True,
|
||||
help="""
|
||||
@ -650,6 +649,10 @@ class NotebookApp(BaseIPythonApplication):
|
||||
without warning between this version of IPython and the next stable one.
|
||||
""")
|
||||
|
||||
login_handler = DottedObjectName('IPython.html.auth.login.LoginHandler',
|
||||
config=True,
|
||||
help='The login handler class to use.')
|
||||
|
||||
trust_xheaders = Bool(False, config=True,
|
||||
help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
|
||||
"sent by the upstream reverse proxy. Necessary if the proxy handles SSL")
|
||||
@ -700,7 +703,6 @@ class NotebookApp(BaseIPythonApplication):
|
||||
# setting App.notebook_dir implies setting notebook and kernel dirs as well
|
||||
self.config.FileContentsManager.root_dir = new
|
||||
self.config.MappingKernelManager.root_dir = new
|
||||
|
||||
|
||||
def parse_command_line(self, argv=None):
|
||||
super(NotebookApp, self).parse_command_line(argv)
|
||||
@ -748,6 +750,7 @@ class NotebookApp(BaseIPythonApplication):
|
||||
kls = import_item(self.cluster_manager_class)
|
||||
self.cluster_manager = kls(parent=self, log=self.log)
|
||||
self.cluster_manager.update_profiles()
|
||||
self.login_handler_class = import_item(self.login_handler)
|
||||
|
||||
kls = import_item(self.config_manager_class)
|
||||
self.config_manager = kls(parent=self, log=self.log,
|
||||
@ -788,17 +791,10 @@ class NotebookApp(BaseIPythonApplication):
|
||||
ssl_options['keyfile'] = self.keyfile
|
||||
else:
|
||||
ssl_options = None
|
||||
self.web_app.password = self.password
|
||||
self.login_handler_class.validate_notebook_app_security(self, ssl_options=ssl_options)
|
||||
self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options,
|
||||
xheaders=self.trust_xheaders)
|
||||
if not self.ip:
|
||||
warning = "WARNING: The notebook server is listening on all IP addresses"
|
||||
if ssl_options is None:
|
||||
self.log.critical(warning + " and not using encryption. This "
|
||||
"is not recommended.")
|
||||
if not self.password:
|
||||
self.log.critical(warning + " and not using authentication. "
|
||||
"This is highly insecure and not recommended.")
|
||||
|
||||
success = None
|
||||
for port in random_ports(self.port, self.port_retries+1):
|
||||
try:
|
||||
|
Loading…
Reference in New Issue
Block a user