##// END OF EJS Templates
Allow notebook server to run in read-only mode...
MinRK -
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 [self.port] + [self.port + randint(-2*n, 2*n) for i in range(n)]:
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