|
79 | 79 | from nbformat.sign import NotebookNotary
|
80 | 80 | from traitlets import (
|
81 | 81 | Dict, Unicode, Integer, List, Bool, Bytes, Instance,
|
82 |
| - TraitError, Type, Float |
| 82 | + TraitError, Type, Float, observe |
83 | 83 | )
|
84 | 84 | from ipython_genutils import py3compat
|
85 | 85 | from jupyter_core.paths import jupyter_runtime_dir, jupyter_path
|
@@ -533,13 +533,21 @@ def _write_cookie_secret_file(self, secret):
|
533 | 533 | Once used, this token cannot be used again.
|
534 | 534 | """
|
535 | 535 | )
|
| 536 | + |
| 537 | + _token_generated = True |
536 | 538 |
|
537 | 539 | def _token_default(self):
|
538 | 540 | if self.password:
|
539 | 541 | # no token if password is enabled
|
| 542 | + self._token_generated = False |
540 | 543 | return u''
|
541 | 544 | else:
|
| 545 | + self._token_generated = True |
542 | 546 | return binascii.hexlify(os.urandom(24)).decode('ascii')
|
| 547 | + |
| 548 | + @observe('token') |
| 549 | + def _token_changed(self, change): |
| 550 | + self._token_generated = False |
543 | 551 |
|
544 | 552 | password = Unicode(u'', config=True,
|
545 | 553 | help="""Hashed password to use for web authentication.
|
@@ -977,6 +985,12 @@ def init_webapp(self):
|
977 | 985 | @property
|
978 | 986 | def display_url(self):
|
979 | 987 | ip = self.ip if self.ip else '[all ip addresses on your system]'
|
| 988 | + url = self._url(ip) |
| 989 | + if self.token: |
| 990 | + # Don't log full token if it came from config |
| 991 | + token = self.token if self._token_generated else '...' |
| 992 | + url = url_concat(url, {'token': token}) |
| 993 | + return url |
980 | 994 | query = '?token=%s' % self.token if self.token else ''
|
981 | 995 | return self._url(ip) + query
|
982 | 996 |
|
@@ -1195,7 +1209,17 @@ def start(self):
|
1195 | 1209 | b = lambda : browser.open(url_path_join(self.connection_url, uri),
|
1196 | 1210 | new=2)
|
1197 | 1211 | threading.Thread(target=b).start()
|
1198 |
| - |
| 1212 | + |
| 1213 | + if self.token and self._token_generated: |
| 1214 | + # log full URL with generated token, so there's a copy/pasteable link |
| 1215 | + # with auth info. |
| 1216 | + self.log.critical('\n'.join([ |
| 1217 | + '\n', |
| 1218 | + 'Copy/paste this URL into your browser when you connect for the first time,', |
| 1219 | + 'to login with a token:', |
| 1220 | + ' %s' % url_concat(self.connection_url, {'token': self.token}), |
| 1221 | + ])) |
| 1222 | + |
1199 | 1223 | self.io_loop = ioloop.IOLoop.current()
|
1200 | 1224 | if sys.platform.startswith('win'):
|
1201 | 1225 | # add no-op to wake every 5s
|
@@ -1251,4 +1275,3 @@ def list_running_servers(runtime_dir=None):
|
1251 | 1275 | #-----------------------------------------------------------------------------
|
1252 | 1276 |
|
1253 | 1277 | main = launch_new_instance = NotebookApp.launch_instance
|
1254 |
| - |
|
0 commit comments