##// END OF EJS Templates
Add Trust Notebook to File menu
MinRK -
Show More
@@ -275,7 +275,7 b' class FileNotebookManager(NotebookManager):'
275 nb = current.read(f, u'json')
275 nb = current.read(f, u'json')
276 except Exception as e:
276 except Exception as e:
277 raise web.HTTPError(400, u"Unreadable Notebook: %s %s" % (os_path, e))
277 raise web.HTTPError(400, u"Unreadable Notebook: %s %s" % (os_path, e))
278 self.mark_trusted_cells(nb, path, name)
278 self.mark_trusted_cells(nb, name, path)
279 model['content'] = nb
279 model['content'] = nb
280 return model
280 return model
281
281
@@ -300,7 +300,7 b' class FileNotebookManager(NotebookManager):'
300 os_path = self._get_os_path(new_name, new_path)
300 os_path = self._get_os_path(new_name, new_path)
301 nb = current.to_notebook_json(model['content'])
301 nb = current.to_notebook_json(model['content'])
302
302
303 self.check_and_sign(nb, new_path, new_name)
303 self.check_and_sign(nb, new_name, new_path)
304
304
305 if 'name' in nb['metadata']:
305 if 'name' in nb['metadata']:
306 nb['metadata']['name'] = u''
306 nb['metadata']['name'] = u''
@@ -222,6 +222,17 b' class NotebookHandler(IPythonHandler):'
222 self.finish()
222 self.finish()
223
223
224
224
225 class NotebookTrustHandler(IPythonHandler):
226
227 SUPPORTED_METHODS = ('POST')
228
229 @web.authenticated
230 def post(self, path='', name=None):
231 """trust the specified notebook"""
232 self.notebook_manager.trust_notebook(name, path)
233 self.set_status(200)
234
235
225 class NotebookCheckpointsHandler(IPythonHandler):
236 class NotebookCheckpointsHandler(IPythonHandler):
226
237
227 SUPPORTED_METHODS = ('GET', 'POST')
238 SUPPORTED_METHODS = ('GET', 'POST')
@@ -279,6 +290,7 b' class ModifyNotebookCheckpointsHandler(IPythonHandler):'
279 _checkpoint_id_regex = r"(?P<checkpoint_id>[\w-]+)"
290 _checkpoint_id_regex = r"(?P<checkpoint_id>[\w-]+)"
280
291
281 default_handlers = [
292 default_handlers = [
293 (r"/api/notebooks%s/trust" % notebook_path_regex, NotebookTrustHandler),
282 (r"/api/notebooks%s/checkpoints" % notebook_path_regex, NotebookCheckpointsHandler),
294 (r"/api/notebooks%s/checkpoints" % notebook_path_regex, NotebookCheckpointsHandler),
283 (r"/api/notebooks%s/checkpoints/%s" % (notebook_path_regex, _checkpoint_id_regex),
295 (r"/api/notebooks%s/checkpoints/%s" % (notebook_path_regex, _checkpoint_id_regex),
284 ModifyNotebookCheckpointsHandler),
296 ModifyNotebookCheckpointsHandler),
@@ -286,5 +298,3 b' default_handlers = ['
286 (r"/api/notebooks%s" % path_regex, NotebookHandler),
298 (r"/api/notebooks%s" % path_regex, NotebookHandler),
287 ]
299 ]
288
300
289
290
@@ -224,9 +224,18 b' class NotebookManager(LoggingConfigurable):'
224 def log_info(self):
224 def log_info(self):
225 self.log.info(self.info_string())
225 self.log.info(self.info_string())
226
226
227 # NotebookManager methods provided for use in subclasses.
227 def trust_notebook(self, name, path=''):
228
228 """Check for trusted cells, and sign the notebook.
229 def check_and_sign(self, nb, path, name):
229
230 Called as a part of saving notebooks.
231 """
232 model = self.get_notebook(name, path)
233 nb = model['content']
234 self.log.warn("Trusting notebook %s/%s", path, name)
235 self.notary.mark_cells(nb, True)
236 self.save_notebook(model, name, path)
237
238 def check_and_sign(self, nb, name, path=''):
230 """Check for trusted cells, and sign the notebook.
239 """Check for trusted cells, and sign the notebook.
231
240
232 Called as a part of saving notebooks.
241 Called as a part of saving notebooks.
@@ -236,7 +245,7 b' class NotebookManager(LoggingConfigurable):'
236 else:
245 else:
237 self.log.warn("Saving untrusted notebook %s/%s", path, name)
246 self.log.warn("Saving untrusted notebook %s/%s", path, name)
238
247
239 def mark_trusted_cells(self, nb, path, name):
248 def mark_trusted_cells(self, nb, name, path=''):
240 """Mark cells as trusted if the notebook signature matches.
249 """Mark cells as trusted if the notebook signature matches.
241
250
242 Called as a part of loading notebooks.
251 Called as a part of loading notebooks.
@@ -2,12 +2,15 b''
2 """Tests for the notebook manager."""
2 """Tests for the notebook manager."""
3 from __future__ import print_function
3 from __future__ import print_function
4
4
5 import logging
5 import os
6 import os
6
7
7 from tornado.web import HTTPError
8 from tornado.web import HTTPError
8 from unittest import TestCase
9 from unittest import TestCase
9 from tempfile import NamedTemporaryFile
10 from tempfile import NamedTemporaryFile
10
11
12 from IPython.nbformat import current
13
11 from IPython.utils.tempdir import TemporaryDirectory
14 from IPython.utils.tempdir import TemporaryDirectory
12 from IPython.utils.traitlets import TraitError
15 from IPython.utils.traitlets import TraitError
13 from IPython.html.utils import url_path_join
16 from IPython.html.utils import url_path_join
@@ -55,6 +58,14 b' class TestFileNotebookManager(TestCase):'
55
58
56 class TestNotebookManager(TestCase):
59 class TestNotebookManager(TestCase):
57
60
61 def setUp(self):
62 self._temp_dir = TemporaryDirectory()
63 self.td = self._temp_dir.name
64 self.nbm = FileNotebookManager(notebook_dir=self.td, log=logging.getLogger())
65
66 def tearDown(self):
67 self._temp_dir.cleanup()
68
58 def make_dir(self, abs_path, rel_path):
69 def make_dir(self, abs_path, rel_path):
59 """make subdirectory, rel_path is the relative path
70 """make subdirectory, rel_path is the relative path
60 to that directory from the location where the server started"""
71 to that directory from the location where the server started"""
@@ -63,11 +74,33 b' class TestNotebookManager(TestCase):'
63 os.makedirs(os_path)
74 os.makedirs(os_path)
64 except OSError:
75 except OSError:
65 print("Directory already exists: %r" % os_path)
76 print("Directory already exists: %r" % os_path)
66
77
78 def add_code_cell(self, nb):
79 output = current.new_output("display_data", output_javascript="alert('hi');")
80 cell = current.new_code_cell("print('hi')", outputs=[output])
81 if not nb.worksheets:
82 nb.worksheets.append(current.new_worksheet())
83 nb.worksheets[0].cells.append(cell)
84
85 def new_notebook(self):
86 nbm = self.nbm
87 model = nbm.create_notebook()
88 name = model['name']
89 path = model['path']
90
91 full_model = nbm.get_notebook(name, path)
92 nb = full_model['content']
93 self.add_code_cell(nb)
94
95 nbm.save_notebook(full_model, name, path)
96 return nb, name, path
97
67 def test_create_notebook(self):
98 def test_create_notebook(self):
68 with TemporaryDirectory() as td:
99 with TemporaryDirectory() as td:
69 # Test in root directory
100 # Test in root directory
101 # Create a notebook
70 nm = FileNotebookManager(notebook_dir=td)
102 nm = FileNotebookManager(notebook_dir=td)
103 # Test in root directory
71 model = nm.create_notebook()
104 model = nm.create_notebook()
72 assert isinstance(model, dict)
105 assert isinstance(model, dict)
73 self.assertIn('name', model)
106 self.assertIn('name', model)
@@ -243,3 +276,43 b' class TestNotebookManager(TestCase):'
243 copy2 = nm.copy_notebook(name, u'copy 2.ipynb', path=path)
276 copy2 = nm.copy_notebook(name, u'copy 2.ipynb', path=path)
244 self.assertEqual(copy2['name'], u'copy 2.ipynb')
277 self.assertEqual(copy2['name'], u'copy 2.ipynb')
245
278
279 def test_trust_notebook(self):
280 nbm = self.nbm
281 nb, name, path = self.new_notebook()
282
283 untrusted = nbm.get_notebook(name, path)['content']
284 assert not nbm.notary.check_signature(untrusted)
285
286 nbm.trust_notebook(name, path)
287 trusted = nbm.get_notebook(name, path)['content']
288 assert nbm.notary.check_signature(trusted)
289
290 def test_mark_trusted_cells(self):
291 nbm = self.nbm
292 nb, name, path = self.new_notebook()
293
294 nbm.mark_trusted_cells(nb, name, path)
295 for cell in nb.worksheets[0].cells:
296 if cell.cell_type == 'code':
297 assert not cell.trusted
298
299 nbm.trust_notebook(name, path)
300 nb = nbm.get_notebook(name, path)['content']
301 nbm.mark_trusted_cells(nb, name, path)
302 for cell in nb.worksheets[0].cells:
303 if cell.cell_type == 'code':
304 assert cell.trusted
305
306 def test_check_and_sign(self):
307 nbm = self.nbm
308 nb, name, path = self.new_notebook()
309
310 nbm.mark_trusted_cells(nb, name, path)
311 nbm.check_and_sign(nb, name, path)
312 assert not nbm.notary.check_signature(nb)
313
314 nbm.trust_notebook(name, path)
315 nb = nbm.get_notebook(name, path)['content']
316 nbm.mark_trusted_cells(nb, name, path)
317 nbm.check_and_sign(nb, name, path)
318 assert nbm.notary.check_signature(nb)
@@ -133,6 +133,9 b' var IPython = (function (IPython) {'
133 });
133 });
134 this.element.find('#restore_checkpoint').click(function () {
134 this.element.find('#restore_checkpoint').click(function () {
135 });
135 });
136 this.element.find('#trust_notebook').click(function () {
137 IPython.notebook.trust_notebook();
138 });
136 this.element.find('#kill_and_exit').click(function () {
139 this.element.find('#kill_and_exit').click(function () {
137 IPython.notebook.session.delete();
140 IPython.notebook.session.delete();
138 setTimeout(function(){
141 setTimeout(function(){
@@ -1786,6 +1786,75 b' var IPython = (function (IPython) {'
1786 $([IPython.events]).trigger('notebook_save_failed.Notebook', [xhr, status, error]);
1786 $([IPython.events]).trigger('notebook_save_failed.Notebook', [xhr, status, error]);
1787 };
1787 };
1788
1788
1789 /**
1790 * Explicitly trust the output of this notebook.
1791 *
1792 * @method trust_notebook
1793 */
1794 Notebook.prototype.trust_notebook = function (extra_settings) {
1795 // We do the call with settings so we can set cache to false.
1796
1797 var settings = {
1798 processData : false,
1799 cache : false,
1800 type : "POST",
1801 success : $.proxy(this._trust_notebook_success, this),
1802 error : $.proxy(this._trust_notebook_error, this)
1803 };
1804 if (extra_settings) {
1805 for (var key in extra_settings) {
1806 settings[key] = extra_settings[key];
1807 }
1808 }
1809
1810 var body = $("<div>").append($("<p>")
1811 .text("A trusted IPython notebook may execute hidden malicious code ")
1812 .append($("<strong>")
1813 .append(
1814 $("<em>").text("when you open it")
1815 )
1816 ).append(".")
1817 ).append($("<p>")
1818 .text("For more information, see the ")
1819 .append($("<a>").attr("href", "http://ipython.org/security.html")
1820 .text("IPython security documentation")
1821 ).append(".")
1822 );
1823
1824 var nb = this;
1825 IPython.dialog.modal({
1826 title: "Trust this notebook?",
1827 body: body,
1828
1829 buttons: {
1830 Cancel : {},
1831 Trust : {
1832 class : "btn-danger",
1833 click : function () {
1834 $([IPython.events]).trigger('notebook_trusting.Notebook');
1835 var url = utils.url_join_encode(
1836 nb.base_url,
1837 'api/notebooks',
1838 nb.notebook_path,
1839 nb.notebook_name,
1840 'trust'
1841 );
1842 $.ajax(url, settings);
1843 }
1844 }
1845 }
1846 });
1847 };
1848
1849 Notebook.prototype._trust_notebook_success = function (data, status, xhr) {
1850 $([IPython.events]).trigger('notebook_trusted.Notebook');
1851 window.location.reload();
1852 };
1853
1854 Notebook.prototype._trust_notebook_error = function (xhr, status, error) {
1855 $([IPython.events]).trigger('notebook_trust_failed.Notebook', [xhr, status, error]);
1856 };
1857
1789 Notebook.prototype.new_notebook = function(){
1858 Notebook.prototype.new_notebook = function(){
1790 var path = this.notebook_path;
1859 var path = this.notebook_path;
1791 var base_url = this.base_url;
1860 var base_url = this.base_url;
@@ -86,7 +86,10 b' class="notebook_app"'
86 </ul>
86 </ul>
87 </li>
87 </li>
88 <li class="divider"></li>
88 <li class="divider"></li>
89
89 <li id="trust_notebook"
90 title="Trust the output of this notebook">
91 <a href="#" >Trust Notebook</a></li>
92 <li class="divider"></li>
90 <li id="kill_and_exit"
93 <li id="kill_and_exit"
91 title="Shutdown this notebook's kernel, and close this window">
94 title="Shutdown this notebook's kernel, and close this window">
92 <a href="#" >Close and halt</a></li>
95 <a href="#" >Close and halt</a></li>
General Comments 0
You need to be logged in to leave comments. Login now