From dc311c51a83933888fd7eb30ff3437b158960f53 Mon Sep 17 00:00:00 2001 From: "Maarten A. Breddels" Date: Mon, 30 Nov 2015 16:53:09 +0100 Subject: [PATCH 1/3] new feature: NotebookApp.password_required, when enabled will only run with a password present. If not present, will ask the user for it. This is useful in a multi user environment, for instance when everybody in the LAN can access each other's machine though ssh. --- notebook/notebookapp.py | 65 +++++++++++++++++++++++++++++- notebook/tests/test_notebookapp.py | 5 +++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/notebook/notebookapp.py b/notebook/notebookapp.py index 4f112d500..63afce541 100644 --- a/notebook/notebookapp.py +++ b/notebook/notebookapp.py @@ -58,6 +58,15 @@ from notebook import ( DEFAULT_TEMPLATE_PATH_LIST, __version__, ) +from .auth import passwd +from getpass import getpass + +# py23 compatibility +try: + raw_input = raw_input +except NameError: + raw_input = input + from .base.handlers import Template404 from .log import log_request from .services.kernels.kernelmanager import MappingKernelManager @@ -570,6 +579,17 @@ class NotebookApp(JupyterApp): """ ) + password_required = Bool(False, config=True, + help="""Forces users to use a password for the Notebook server. + This is useful in a multi user environment, for instance when + everybody in the LAN can access each other's machine though ssh. + + In such a case, server the notebook server on localhost is not secure + since any user can connect to the notebook server via ssh. + + """ + ) + open_browser = Bool(True, config=True, help="""Whether to open in a browser after starting. The specific browser used is platform dependent and @@ -917,7 +937,50 @@ class NotebookApp(JupyterApp): # ensure default_url starts with base_url if not self.default_url.startswith(self.base_url): self.default_url = url_path_join(self.base_url, self.default_url) - + + if self.password_required and (not self.password): + self.log.critical("Notebook servers are configured to only be run with a password. Please provide a password") + done = False + while not done: + password = password1 = getpass("Provide password: ") + password_repeat = getpass("Repeat password: ") + if password != password_repeat: + print("Passwords do not match, try again") + elif len(password) < 4: + print("Please provide at least 4 characters") + else: + done = True + + password_hash = passwd(password) + self.password = password_hash + configure_code = """c.NotebookApp.password = u%r""" % password_hash + + give_store_hint = True + + # This is copied from jupyter_core.application.Application.write_default_config + if self.config_file: + config_file = self.config_file + else: + config_file = os.path.join(self.config_dir, self.config_file_name + '.py') + + if raw_input("Save password hash in %s? [y/n]" % config_file).lower() == "y": + + if not os.path.exists(config_file): + self.write_default_config() + + if not os.path.exists(config_file): + self.log.critical("Cannot find %s, while it should have been generated" % config_file) + else: + with open(config_file, "a") as f: + f.write("\n%s\n" % configure_code) + give_store_hint = False + if give_store_hint: + print("Password hash is: %s, please put the following line in your IPython notebook configuration file: " % password_hash) + print(configure_code) + print("For instance in ipython_notebook_config.py by executing this on the command line:") + print("$ echo \"c = get_config();%s\" >> %s" % (configure_code, config_file)) + + self.web_app = NotebookWebApplication( self, self.kernel_manager, self.contents_manager, self.session_manager, self.kernel_spec_manager, diff --git a/notebook/tests/test_notebookapp.py b/notebook/tests/test_notebookapp.py index 270ec1b0f..47843b9a5 100644 --- a/notebook/tests/test_notebookapp.py +++ b/notebook/tests/test_notebookapp.py @@ -14,6 +14,11 @@ from jupyter_core.application import NoStart from ipython_genutils.tempdir import TemporaryDirectory from traitlets import TraitError from notebook import notebookapp, __version__ +from tornado import httpserver +from notebook.auth import passwd +import tempfile + + NotebookApp = notebookapp.NotebookApp From b9d106a808027bdfd13038808a71c226485483bb Mon Sep 17 00:00:00 2001 From: "Maarten A. Breddels" Date: Mon, 7 Mar 2016 13:55:13 +0100 Subject: [PATCH 2/3] implemented changes as discussed at PR 775, password generation moved to module --- notebook/auth/__main__.py | 42 ++++++++++++++++++++++++++++ notebook/notebookapp.py | 45 +++--------------------------- notebook/tests/test_notebookapp.py | 7 +---- 3 files changed, 47 insertions(+), 47 deletions(-) create mode 100644 notebook/auth/__main__.py diff --git a/notebook/auth/__main__.py b/notebook/auth/__main__.py new file mode 100644 index 000000000..602fb7e0e --- /dev/null +++ b/notebook/auth/__main__.py @@ -0,0 +1,42 @@ +from notebook.auth import passwd +from getpass import getpass +from traitlets.config.manager import BaseJSONConfigManager +from jupyter_core.paths import jupyter_config_dir +import argparse +import sys + +def set_password(args): + password = args.password + while not password : + password1 = getpass("" if args.quiet else "Provide password: ") + password_repeat = getpass("" if args.quiet else "Repeat password: ") + if password1 != password_repeat: + print("Passwords do not match, try again") + elif len(password1) < 4: + print("Please provide at least 4 characters") + else: + password = password1 + + password_hash = passwd(password) + cfg = BaseJSONConfigManager(config_dir=jupyter_config_dir()) + cfg.update('jupyter_notebook_config', { + 'NotebookApp': { + 'password': password_hash, + } + }) + if not args.quiet: + print("password stored in config dir: %s" % jupyter_config_dir()) + +def main(argv): + parser = argparse.ArgumentParser(argv[0]) + subparsers = parser.add_subparsers() + parser_password = subparsers.add_parser('password', help='create soneira peebles dataset') + parser_password.add_argument("password", help="password to set, if not given, a password will be queried for (NOTE: this may not be safe)", + nargs="?") + parser_password.add_argument("--quiet", help="Supress messages", action="store_true") + parser_password.set_defaults(function=set_password) + args = parser.parse_args(argv[1:]) + args.function(args) + +if __name__ == "__main__": + main(sys.argv) \ No newline at end of file diff --git a/notebook/notebookapp.py b/notebook/notebookapp.py index 63afce541..48deca9fc 100644 --- a/notebook/notebookapp.py +++ b/notebook/notebookapp.py @@ -939,47 +939,10 @@ class NotebookApp(JupyterApp): self.default_url = url_path_join(self.base_url, self.default_url) if self.password_required and (not self.password): - self.log.critical("Notebook servers are configured to only be run with a password. Please provide a password") - done = False - while not done: - password = password1 = getpass("Provide password: ") - password_repeat = getpass("Repeat password: ") - if password != password_repeat: - print("Passwords do not match, try again") - elif len(password) < 4: - print("Please provide at least 4 characters") - else: - done = True - - password_hash = passwd(password) - self.password = password_hash - configure_code = """c.NotebookApp.password = u%r""" % password_hash - - give_store_hint = True - - # This is copied from jupyter_core.application.Application.write_default_config - if self.config_file: - config_file = self.config_file - else: - config_file = os.path.join(self.config_dir, self.config_file_name + '.py') - - if raw_input("Save password hash in %s? [y/n]" % config_file).lower() == "y": - - if not os.path.exists(config_file): - self.write_default_config() - - if not os.path.exists(config_file): - self.log.critical("Cannot find %s, while it should have been generated" % config_file) - else: - with open(config_file, "a") as f: - f.write("\n%s\n" % configure_code) - give_store_hint = False - if give_store_hint: - print("Password hash is: %s, please put the following line in your IPython notebook configuration file: " % password_hash) - print(configure_code) - print("For instance in ipython_notebook_config.py by executing this on the command line:") - print("$ echo \"c = get_config();%s\" >> %s" % (configure_code, config_file)) - + self.log.critical("Notebook servers are configured to only be run with a password.") + self.log.critical("Hint: run the following command to set a password") + self.log.critical("\t$ python -m notebook.auth password") + sys.exit(1) self.web_app = NotebookWebApplication( self, self.kernel_manager, self.contents_manager, diff --git a/notebook/tests/test_notebookapp.py b/notebook/tests/test_notebookapp.py index 47843b9a5..afb6d921a 100644 --- a/notebook/tests/test_notebookapp.py +++ b/notebook/tests/test_notebookapp.py @@ -14,11 +14,7 @@ from jupyter_core.application import NoStart from ipython_genutils.tempdir import TemporaryDirectory from traitlets import TraitError from notebook import notebookapp, __version__ -from tornado import httpserver -from notebook.auth import passwd -import tempfile - - +from notebook import notebookapp NotebookApp = notebookapp.NotebookApp @@ -86,7 +82,6 @@ def test_generate_config(): app.start() assert os.path.exists(os.path.join(td, 'jupyter_notebook_config.py')) - #test if the version testin function works def test_pep440_version(): From 5a3272f77174e7972350ca02110601f06f55c710 Mon Sep 17 00:00:00 2001 From: "Maarten A. Breddels" Date: Mon, 14 Mar 2016 14:10:55 +0100 Subject: [PATCH 3/3] fixed a typo, and corrected the help text --- notebook/auth/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notebook/auth/__main__.py b/notebook/auth/__main__.py index 602fb7e0e..023983975 100644 --- a/notebook/auth/__main__.py +++ b/notebook/auth/__main__.py @@ -30,10 +30,10 @@ def set_password(args): def main(argv): parser = argparse.ArgumentParser(argv[0]) subparsers = parser.add_subparsers() - parser_password = subparsers.add_parser('password', help='create soneira peebles dataset') + parser_password = subparsers.add_parser('password', help='sets a password for your notebook server') parser_password.add_argument("password", help="password to set, if not given, a password will be queried for (NOTE: this may not be safe)", nargs="?") - parser_password.add_argument("--quiet", help="Supress messages", action="store_true") + parser_password.add_argument("--quiet", help="suppress messages", action="store_true") parser_password.set_defaults(function=set_password) args = parser.parse_args(argv[1:]) args.function(args)