Skip to content

Commit 64fa7cf

Browse files
authored
Merge pull request #4210 from SylvainCorlay/message-filtering
Enable kernel message filtering
2 parents 9d19aa3 + 4a71514 commit 64fa7cf

File tree

4 files changed

+119
-12
lines changed

4 files changed

+119
-12
lines changed

notebook/services/kernels/handlers.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,13 @@ def on_message(self, msg):
306306
if channel not in self.channels:
307307
self.log.warning("No such channel: %r", channel)
308308
return
309-
stream = self.channels[channel]
310-
self.session.send(stream, msg)
309+
am = self.kernel_manager.allowed_message_types
310+
mt = msg['header']['msg_type']
311+
if am and mt not in am:
312+
self.log.warning('Received message of type "%s", which is not allowed. Ignoring.' % mt)
313+
else:
314+
stream = self.channels[channel]
315+
self.session.send(stream, msg)
311316

312317
def _on_zmq_reply(self, stream, msg_list):
313318
idents, fed_msg_list = self.session.feed_identities(msg_list)

notebook/services/kernels/kernelmanager.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def _default_kernel_manager_class(self):
3939
kernel_argv = List(Unicode())
4040

4141
root_dir = Unicode(config=True)
42-
42+
4343
_kernel_connections = Dict()
4444

4545
_culler_callback = None
@@ -95,15 +95,15 @@ def _update_root_dir(self, proposal):
9595
no frontends are connected.
9696
"""
9797
)
98-
98+
9999
kernel_info_timeout = Float(60, config=True,
100100
help="""Timeout for giving up on a kernel (in seconds).
101101
102102
On starting and restarting kernels, we check whether the
103103
kernel is running and responsive by sending kernel_info_requests.
104104
This sets the timeout in seconds for how long the kernel can take
105-
before being presumed dead.
106-
This affects the MappingKernelManager (which handles kernel restarts)
105+
before being presumed dead.
106+
This affects the MappingKernelManager (which handles kernel restarts)
107107
and the ZMQChannelsHandler (which handles the startup).
108108
"""
109109
)
@@ -120,6 +120,12 @@ def __init__(self, **kwargs):
120120
super(MappingKernelManager, self).__init__(**kwargs)
121121
self.last_kernel_activity = utcnow()
122122

123+
allowed_message_types = List(trait=Unicode(), config=True,
124+
help="""White list of allowed kernel message types.
125+
When the list is empty, all message types are allowed.
126+
"""
127+
)
128+
123129
#-------------------------------------------------------------------------
124130
# Methods for managing kernels and sessions
125131
#-------------------------------------------------------------------------
@@ -304,32 +310,32 @@ def restart_kernel(self, kernel_id):
304310
# return a Future that will resolve when the kernel has successfully restarted
305311
channel = kernel.connect_shell()
306312
future = Future()
307-
313+
308314
def finish():
309315
"""Common cleanup when restart finishes/fails for any reason."""
310316
if not channel.closed():
311317
channel.close()
312318
loop.remove_timeout(timeout)
313319
kernel.remove_restart_callback(on_restart_failed, 'dead')
314-
320+
315321
def on_reply(msg):
316322
self.log.debug("Kernel info reply received: %s", kernel_id)
317323
finish()
318324
if not future.done():
319325
future.set_result(msg)
320-
326+
321327
def on_timeout():
322328
self.log.warning("Timeout waiting for kernel_info_reply: %s", kernel_id)
323329
finish()
324330
if not future.done():
325331
future.set_exception(gen.TimeoutError("Timeout waiting for restart"))
326-
332+
327333
def on_restart_failed():
328334
self.log.warning("Restarting kernel failed: %s", kernel_id)
329335
finish()
330336
if not future.done():
331337
future.set_exception(RuntimeError("Restart failed"))
332-
338+
333339
kernel.add_restart_callback(on_restart_failed, 'dead')
334340
kernel.session.send(channel, "kernel_info_request")
335341
channel.on_recv(on_reply)
@@ -383,7 +389,7 @@ def _check_kernel_id(self, kernel_id):
383389

384390
def start_watching_activity(self, kernel_id):
385391
"""Start watching IOPub messages on a kernel for activity.
386-
392+
387393
- update last_activity on every message
388394
- record execution_state from status messages
389395
"""

notebook/services/kernels/tests/test_kernels_api.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import json
44
import time
55

6+
from traitlets.config import Config
7+
68
from tornado.httpclient import HTTPRequest
79
from tornado.ioloop import IOLoop
810
from tornado.websocket import websocket_connect
@@ -12,6 +14,7 @@
1214
from notebook.utils import url_path_join
1315
from notebook.tests.launchnotebook import NotebookTestBase, assert_http_error
1416

17+
1518
class KernelAPI(object):
1619
"""Wrapper for kernel REST API requests"""
1720
def __init__(self, request, base_url, headers):
@@ -183,3 +186,19 @@ def test_connections(self):
183186
break
184187
model = self.kern_api.get(kid).json()
185188
self.assertEqual(model['connections'], 0)
189+
190+
191+
class KernelFilterTest(NotebookTestBase):
192+
193+
# A special install of NotebookTestBase where only `kernel_info_request`
194+
# messages are allowed.
195+
config = Config({
196+
'NotebookApp': {
197+
'MappingKernelManager': {
198+
'allowed_message_types': ['kernel_info_request']
199+
}
200+
}
201+
})
202+
# Sanity check verifying that the configurable was properly set.
203+
def test_config(self):
204+
self.assertEqual(self.notebook.kernel_manager.allowed_message_types, ['kernel_info_request'])

notebook/tree/tests/handlers.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""Tornado handlers for the tree view."""
2+
3+
# Copyright (c) Jupyter Development Team.
4+
# Distributed under the terms of the Modified BSD License.
5+
6+
from tornado import web
7+
import os
8+
from ..base.handlers import IPythonHandler, path_regex
9+
from ..utils import url_path_join, url_escape
10+
11+
12+
class TreeHandler(IPythonHandler):
13+
"""Render the tree view, listing notebooks, etc."""
14+
15+
def generate_breadcrumbs(self, path):
16+
breadcrumbs = [(url_path_join(self.base_url, 'tree'), '')]
17+
parts = path.split('/')
18+
for i in range(len(parts)):
19+
if parts[i]:
20+
link = url_path_join(self.base_url, 'tree',
21+
url_escape(url_path_join(*parts[:i+1])),
22+
)
23+
breadcrumbs.append((link, parts[i]))
24+
return breadcrumbs
25+
26+
def generate_page_title(self, path):
27+
comps = path.split('/')
28+
if len(comps) > 3:
29+
for i in range(len(comps)-2):
30+
comps.pop(0)
31+
page_title = url_path_join(*comps)
32+
if page_title:
33+
return page_title+'/'
34+
else:
35+
return 'Home'
36+
37+
@web.authenticated
38+
def get(self, path=''):
39+
path = path.strip('/')
40+
cm = self.contents_manager
41+
42+
if cm.dir_exists(path=path):
43+
if cm.is_hidden(path) and not cm.allow_hidden:
44+
self.log.info("Refusing to serve hidden directory, via 404 Error")
45+
raise web.HTTPError(404)
46+
breadcrumbs = self.generate_breadcrumbs(path)
47+
page_title = self.generate_page_title(path)
48+
self.write(self.render_template('tree.html',
49+
page_title=page_title,
50+
notebook_path=path,
51+
breadcrumbs=breadcrumbs,
52+
terminals_available=self.settings['terminals_available'],
53+
server_root=self.settings['server_root_dir'],
54+
))
55+
elif cm.file_exists(path):
56+
# it's not a directory, we have redirecting to do
57+
model = cm.get(path, content=False)
58+
# redirect to /api/notebooks if it's a notebook, otherwise /api/files
59+
service = 'notebooks' if model['type'] == 'notebook' else 'files'
60+
url = url_path_join(
61+
self.base_url, service, url_escape(path),
62+
)
63+
self.log.debug("Redirecting %s to %s", self.request.path, url)
64+
self.redirect(url)
65+
else:
66+
raise web.HTTPError(404)
67+
68+
69+
#-----------------------------------------------------------------------------
70+
# URL to handler mappings
71+
#-----------------------------------------------------------------------------
72+
73+
74+
default_handlers = [
75+
(r"/tree%s" % path_regex, TreeHandler),
76+
(r"/tree", TreeHandler),
77+
]

0 commit comments

Comments
 (0)