##// END OF EJS Templates
sign notebooks
MinRK -
Show More
@@ -26,7 +26,7 b' import shutil'
26 from tornado import web
26 from tornado import web
27
27
28 from .nbmanager import NotebookManager
28 from .nbmanager import NotebookManager
29 from IPython.nbformat import current
29 from IPython.nbformat import current, sign
30 from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError
30 from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError
31 from IPython.utils import tz
31 from IPython.utils import tz
32
32
@@ -207,13 +207,14 b' class FileNotebookManager(NotebookManager):'
207 model['path'] = path
207 model['path'] = path
208 model['last_modified'] = last_modified
208 model['last_modified'] = last_modified
209 model['created'] = created
209 model['created'] = created
210 if content is True:
210 if content:
211 with io.open(os_path, 'r', encoding='utf-8') as f:
211 with io.open(os_path, 'r', encoding='utf-8') as f:
212 try:
212 try:
213 nb = current.read(f, u'json')
213 nb = current.read(f, u'json')
214 except Exception as e:
214 except Exception as e:
215 raise web.HTTPError(400, u"Unreadable Notebook: %s %s" % (os_path, e))
215 raise web.HTTPError(400, u"Unreadable Notebook: %s %s" % (os_path, e))
216 model['content'] = nb
216 model['content'] = nb
217 sign.mark_trusted_cells(nb, self.secret)
217 return model
218 return model
218
219
219 def save_notebook_model(self, model, name='', path=''):
220 def save_notebook_model(self, model, name='', path=''):
@@ -236,6 +237,10 b' class FileNotebookManager(NotebookManager):'
236 # Save the notebook file
237 # Save the notebook file
237 os_path = self.get_os_path(new_name, new_path)
238 os_path = self.get_os_path(new_name, new_path)
238 nb = current.to_notebook_json(model['content'])
239 nb = current.to_notebook_json(model['content'])
240
241 if sign.check_trusted_cells(nb):
242 sign.trust_notebook(nb, self.secret, self.signature_scheme)
243
239 if 'name' in nb['metadata']:
244 if 'name' in nb['metadata']:
240 nb['metadata']['name'] = u''
245 nb['metadata']['name'] = u''
241 try:
246 try:
@@ -17,12 +17,16 b' Authors:'
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 import base64
21 import hashlib
22 import io
20 import os
23 import os
21
24
22 from IPython.config.configurable import LoggingConfigurable
25 from IPython.config.configurable import LoggingConfigurable
26 from IPython.core.application import BaseIPythonApplication
23 from IPython.nbformat import current
27 from IPython.nbformat import current
24 from IPython.utils import py3compat
28 from IPython.utils import py3compat
25 from IPython.utils.traitlets import Unicode, TraitError
29 from IPython.utils.traitlets import Unicode, TraitError, Enum, Bytes
26
30
27 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
28 # Classes
32 # Classes
@@ -42,6 +46,35 b' class NotebookManager(LoggingConfigurable):'
42
46
43 filename_ext = Unicode(u'.ipynb')
47 filename_ext = Unicode(u'.ipynb')
44
48
49 signature_scheme = Enum(hashlib.algorithms, default_value='sha256', config=True,
50 help="""The signature scheme used to sign notebooks."""
51 )
52
53 secret = Bytes(config=True,
54 help="""The secret key with which notebooks are signed."""
55 )
56 def _secret_default(self):
57 # note : this assumes an Application is running
58 profile_dir = BaseIPythonApplication.instance().profile_dir
59 secret_file = os.path.join(profile_dir.security_dir, 'notebook_secret')
60 if os.path.exists(secret_file):
61 with io.open(secret_file, 'rb') as f:
62 return f.read()
63 else:
64 secret = base64.encodestring(os.urandom(1024))
65 self.log.info("Writing output secret to %s", secret_file)
66 with io.open(secret_file, 'wb') as f:
67 f.write(secret)
68 try:
69 os.chmod(secret_file, 0o600)
70 except OSError:
71 self.log.warn(
72 "Could not set permissions on %s",
73 secret_file
74 )
75 return secret
76
77
45 def path_exists(self, path):
78 def path_exists(self, path):
46 """Does the API-style path (directory) actually exist?
79 """Does the API-style path (directory) actually exist?
47
80
@@ -530,6 +530,7 b' var IPython = (function (IPython) {'
530 } else {
530 } else {
531 this.set_input_prompt();
531 this.set_input_prompt();
532 }
532 }
533 this.output_area.trusted = data.trusted || false;
533 this.output_area.fromJSON(data.outputs);
534 this.output_area.fromJSON(data.outputs);
534 if (data.collapsed !== undefined) {
535 if (data.collapsed !== undefined) {
535 if (data.collapsed) {
536 if (data.collapsed) {
@@ -552,6 +553,7 b' var IPython = (function (IPython) {'
552 var outputs = this.output_area.toJSON();
553 var outputs = this.output_area.toJSON();
553 data.outputs = outputs;
554 data.outputs = outputs;
554 data.language = 'python';
555 data.language = 'python';
556 data.trusted = this.output_area.trusted;
555 data.collapsed = this.collapsed;
557 data.collapsed = this.collapsed;
556 return data;
558 return data;
557 };
559 };
@@ -31,6 +31,7 b' var IPython = (function (IPython) {'
31 this.outputs = [];
31 this.outputs = [];
32 this.collapsed = false;
32 this.collapsed = false;
33 this.scrolled = false;
33 this.scrolled = false;
34 this.trusted = true;
34 this.clear_queued = null;
35 this.clear_queued = null;
35 if (prompt_area === undefined) {
36 if (prompt_area === undefined) {
36 this.prompt_area = true;
37 this.prompt_area = true;
@@ -309,7 +310,7 b' var IPython = (function (IPython) {'
309 });
310 });
310 return json;
311 return json;
311 };
312 };
312
313
313 OutputArea.prototype.append_output = function (json) {
314 OutputArea.prototype.append_output = function (json) {
314 this.expand();
315 this.expand();
315 // Clear the output if clear is queued.
316 // Clear the output if clear is queued.
@@ -331,6 +332,7 b' var IPython = (function (IPython) {'
331 } else if (json.output_type === 'stream') {
332 } else if (json.output_type === 'stream') {
332 this.append_stream(json);
333 this.append_stream(json);
333 }
334 }
335
334 this.outputs.push(json);
336 this.outputs.push(json);
335
337
336 // Only reset the height to automatic if the height is currently
338 // Only reset the height to automatic if the height is currently
@@ -526,12 +528,26 b' var IPython = (function (IPython) {'
526 'text/plain'
528 'text/plain'
527 ];
529 ];
528
530
531 OutputArea.safe_outputs = {
532 'text/plain' : true,
533 'image/png' : true,
534 'image/jpeg' : true
535 };
536
529 OutputArea.prototype.append_mime_type = function (json, element) {
537 OutputArea.prototype.append_mime_type = function (json, element) {
530
531 for (var type_i in OutputArea.display_order) {
538 for (var type_i in OutputArea.display_order) {
532 var type = OutputArea.display_order[type_i];
539 var type = OutputArea.display_order[type_i];
533 var append = OutputArea.append_map[type];
540 var append = OutputArea.append_map[type];
534 if ((json[type] !== undefined) && append) {
541 if ((json[type] !== undefined) && append) {
542 if (!this.trusted && !OutputArea.safe_outputs[type]) {
543 // not trusted show warning and do not display
544 var content = {
545 text : "Untrusted " + type + " output ignored.",
546 stream : "stderr"
547 }
548 this.append_stream(content);
549 continue;
550 }
535 var md = json.metadata || {};
551 var md = json.metadata || {};
536 append.apply(this, [json[type], md, element]);
552 append.apply(this, [json[type], md, element]);
537 return true;
553 return true;
@@ -757,6 +773,7 b' var IPython = (function (IPython) {'
757 // clear all, no need for logic
773 // clear all, no need for logic
758 this.element.html("");
774 this.element.html("");
759 this.outputs = [];
775 this.outputs = [];
776 this.trusted = true;
760 this.unscroll_area();
777 this.unscroll_area();
761 return;
778 return;
762 };
779 };
@@ -769,13 +786,6 b' var IPython = (function (IPython) {'
769 var len = outputs.length;
786 var len = outputs.length;
770 var data;
787 var data;
771
788
772 // We don't want to display javascript on load, so remove it from the
773 // display order for the duration of this function call, but be sure to
774 // put it back in there so incoming messages that contain javascript
775 // representations get displayed
776 var js_index = OutputArea.display_order.indexOf('application/javascript');
777 OutputArea.display_order.splice(js_index, 1);
778
779 for (var i=0; i<len; i++) {
789 for (var i=0; i<len; i++) {
780 data = outputs[i];
790 data = outputs[i];
781 var msg_type = data.output_type;
791 var msg_type = data.output_type;
@@ -788,9 +798,6 b' var IPython = (function (IPython) {'
788
798
789 this.append_output(data);
799 this.append_output(data);
790 }
800 }
791
792 // reinsert javascript into display order, see note above
793 OutputArea.display_order.splice(js_index, 0, 'application/javascript');
794 };
801 };
795
802
796
803
General Comments 0
You need to be logged in to leave comments. Login now