Show More
@@ -26,6 +26,7 b' from tornado import websocket' | |||||
26 | from zmq.eventloop import ioloop |
|
26 | from zmq.eventloop import ioloop | |
27 | from zmq.utils import jsonapi |
|
27 | from zmq.utils import jsonapi | |
28 |
|
28 | |||
|
29 | from IPython.external.decorator import decorator | |||
29 | from IPython.zmq.session import Session |
|
30 | from IPython.zmq.session import Session | |
30 |
|
31 | |||
31 | try: |
|
32 | try: | |
@@ -34,6 +35,16 b' except ImportError:' | |||||
34 | publish_string = None |
|
35 | publish_string = None | |
35 |
|
36 | |||
36 |
|
37 | |||
|
38 | #----------------------------------------------------------------------------- | |||
|
39 | # Decorator for disabling read-only handlers | |||
|
40 | #----------------------------------------------------------------------------- | |||
|
41 | ||||
|
42 | @decorator | |||
|
43 | def not_if_readonly(f, self, *args, **kwargs): | |||
|
44 | if self.application.ipython_app.read_only: | |||
|
45 | raise web.HTTPError(403, "Notebook server is read-only") | |||
|
46 | else: | |||
|
47 | return f(self, *args, **kwargs) | |||
37 |
|
48 | |||
38 | #----------------------------------------------------------------------------- |
|
49 | #----------------------------------------------------------------------------- | |
39 | # Top-level handlers |
|
50 | # Top-level handlers | |
@@ -82,6 +93,7 b' class LoginHandler(AuthenticatedHandler):' | |||||
82 |
|
93 | |||
83 | class NewHandler(AuthenticatedHandler): |
|
94 | class NewHandler(AuthenticatedHandler): | |
84 |
|
95 | |||
|
96 | @not_if_readonly | |||
85 | @web.authenticated |
|
97 | @web.authenticated | |
86 | def get(self): |
|
98 | def get(self): | |
87 | nbm = self.application.notebook_manager |
|
99 | nbm = self.application.notebook_manager | |
@@ -118,11 +130,13 b' class NamedNotebookHandler(AuthenticatedHandler):' | |||||
118 |
|
130 | |||
119 | class MainKernelHandler(AuthenticatedHandler): |
|
131 | class MainKernelHandler(AuthenticatedHandler): | |
120 |
|
132 | |||
|
133 | @not_if_readonly | |||
121 | @web.authenticated |
|
134 | @web.authenticated | |
122 | def get(self): |
|
135 | def get(self): | |
123 | km = self.application.kernel_manager |
|
136 | km = self.application.kernel_manager | |
124 | self.finish(jsonapi.dumps(km.kernel_ids)) |
|
137 | self.finish(jsonapi.dumps(km.kernel_ids)) | |
125 |
|
138 | |||
|
139 | @not_if_readonly | |||
126 | @web.authenticated |
|
140 | @web.authenticated | |
127 | def post(self): |
|
141 | def post(self): | |
128 | km = self.application.kernel_manager |
|
142 | km = self.application.kernel_manager | |
@@ -138,6 +152,7 b' class KernelHandler(AuthenticatedHandler):' | |||||
138 |
|
152 | |||
139 | SUPPORTED_METHODS = ('DELETE') |
|
153 | SUPPORTED_METHODS = ('DELETE') | |
140 |
|
154 | |||
|
155 | @not_if_readonly | |||
141 | @web.authenticated |
|
156 | @web.authenticated | |
142 | def delete(self, kernel_id): |
|
157 | def delete(self, kernel_id): | |
143 | km = self.application.kernel_manager |
|
158 | km = self.application.kernel_manager | |
@@ -148,6 +163,7 b' class KernelHandler(AuthenticatedHandler):' | |||||
148 |
|
163 | |||
149 | class KernelActionHandler(AuthenticatedHandler): |
|
164 | class KernelActionHandler(AuthenticatedHandler): | |
150 |
|
165 | |||
|
166 | @not_if_readonly | |||
151 | @web.authenticated |
|
167 | @web.authenticated | |
152 | def post(self, kernel_id, action): |
|
168 | def post(self, kernel_id, action): | |
153 | km = self.application.kernel_manager |
|
169 | km = self.application.kernel_manager | |
@@ -226,6 +242,7 b' class AuthenticatedZMQStreamHandler(ZMQStreamHandler):' | |||||
226 | except: |
|
242 | except: | |
227 | logging.warn("couldn't parse cookie string: %s",msg, exc_info=True) |
|
243 | logging.warn("couldn't parse cookie string: %s",msg, exc_info=True) | |
228 |
|
244 | |||
|
245 | @not_if_readonly | |||
229 | def on_first_message(self, msg): |
|
246 | def on_first_message(self, msg): | |
230 | self._inject_cookie_message(msg) |
|
247 | self._inject_cookie_message(msg) | |
231 | if self.get_current_user() is None: |
|
248 | if self.get_current_user() is None: | |
@@ -369,6 +386,7 b' class NotebookRootHandler(AuthenticatedHandler):' | |||||
369 | files = nbm.list_notebooks() |
|
386 | files = nbm.list_notebooks() | |
370 | self.finish(jsonapi.dumps(files)) |
|
387 | self.finish(jsonapi.dumps(files)) | |
371 |
|
388 | |||
|
389 | @not_if_readonly | |||
372 | @web.authenticated |
|
390 | @web.authenticated | |
373 | def post(self): |
|
391 | def post(self): | |
374 | nbm = self.application.notebook_manager |
|
392 | nbm = self.application.notebook_manager | |
@@ -401,6 +419,7 b' class NotebookHandler(AuthenticatedHandler):' | |||||
401 | self.set_header('Last-Modified', last_mod) |
|
419 | self.set_header('Last-Modified', last_mod) | |
402 | self.finish(data) |
|
420 | self.finish(data) | |
403 |
|
421 | |||
|
422 | @not_if_readonly | |||
404 | @web.authenticated |
|
423 | @web.authenticated | |
405 | def put(self, notebook_id): |
|
424 | def put(self, notebook_id): | |
406 | nbm = self.application.notebook_manager |
|
425 | nbm = self.application.notebook_manager | |
@@ -410,6 +429,7 b' class NotebookHandler(AuthenticatedHandler):' | |||||
410 | self.set_status(204) |
|
429 | self.set_status(204) | |
411 | self.finish() |
|
430 | self.finish() | |
412 |
|
431 | |||
|
432 | @not_if_readonly | |||
413 | @web.authenticated |
|
433 | @web.authenticated | |
414 | def delete(self, notebook_id): |
|
434 | def delete(self, notebook_id): | |
415 | nbm = self.application.notebook_manager |
|
435 | nbm = self.application.notebook_manager |
@@ -116,6 +116,10 b" flags['no-browser']=(" | |||||
116 | {'NotebookApp' : {'open_browser' : False}}, |
|
116 | {'NotebookApp' : {'open_browser' : False}}, | |
117 | "Don't open the notebook in a browser after startup." |
|
117 | "Don't open the notebook in a browser after startup." | |
118 | ) |
|
118 | ) | |
|
119 | flags['read-only'] = ( | |||
|
120 | {'NotebookApp' : {'read_only' : True}}, | |||
|
121 | "Launch the Notebook server in read-only mode, not allowing execution or editing" | |||
|
122 | ) | |||
119 |
|
123 | |||
120 | # the flags that are specific to the frontend |
|
124 | # the flags that are specific to the frontend | |
121 | # these must be scrubbed before being passed to the kernel, |
|
125 | # these must be scrubbed before being passed to the kernel, | |
@@ -203,6 +207,10 b' class NotebookApp(BaseIPythonApplication):' | |||||
203 |
|
207 | |||
204 | open_browser = Bool(True, config=True, |
|
208 | open_browser = Bool(True, config=True, | |
205 | help="Whether to open in a browser after starting.") |
|
209 | help="Whether to open in a browser after starting.") | |
|
210 | ||||
|
211 | read_only = Bool(False, config=True, | |||
|
212 | help="Whether to prevent editing/execution of notebooks." | |||
|
213 | ) | |||
206 |
|
214 | |||
207 | def get_ws_url(self): |
|
215 | def get_ws_url(self): | |
208 | """Return the WebSocket URL for this server.""" |
|
216 | """Return the WebSocket URL for this server.""" | |
@@ -282,7 +290,7 b' class NotebookApp(BaseIPythonApplication):' | |||||
282 | # Try random ports centered around the default. |
|
290 | # Try random ports centered around the default. | |
283 | from random import randint |
|
291 | from random import randint | |
284 | n = 50 # Max number of attempts, keep reasonably large. |
|
292 | n = 50 # Max number of attempts, keep reasonably large. | |
285 |
for port in |
|
293 | for port in range(self.port, self.port+5) + [self.port + randint(-2*n, 2*n) for i in range(n-5)]: | |
286 | try: |
|
294 | try: | |
287 | self.http_server.listen(port, self.ip) |
|
295 | self.http_server.listen(port, self.ip) | |
288 | except socket.error, e: |
|
296 | except socket.error, e: |
General Comments 0
You need to be logged in to leave comments.
Login now