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 |
|
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