Allow notebook server to run in read-only mode

Kernels are never started, and all save/delete/execution handlers raise
403: Forbidden.

/cc @fperez
pull/37/head
MinRK 15 years ago
parent ba96d936ca
commit 9a7fda926d

@ -26,6 +26,7 @@ from tornado import websocket
from zmq.eventloop import ioloop
from zmq.utils import jsonapi
from IPython.external.decorator import decorator
from IPython.zmq.session import Session
try:
@ -34,6 +35,16 @@ except ImportError:
publish_string = None
#-----------------------------------------------------------------------------
# Decorator for disabling read-only handlers
#-----------------------------------------------------------------------------
@decorator
def not_if_readonly(f, self, *args, **kwargs):
if self.application.ipython_app.read_only:
raise web.HTTPError(403, "Notebook server is read-only")
else:
return f(self, *args, **kwargs)
#-----------------------------------------------------------------------------
# Top-level handlers
@ -82,6 +93,7 @@ class LoginHandler(AuthenticatedHandler):
class NewHandler(AuthenticatedHandler):
@not_if_readonly
@web.authenticated
def get(self):
nbm = self.application.notebook_manager
@ -118,11 +130,13 @@ class NamedNotebookHandler(AuthenticatedHandler):
class MainKernelHandler(AuthenticatedHandler):
@not_if_readonly
@web.authenticated
def get(self):
km = self.application.kernel_manager
self.finish(jsonapi.dumps(km.kernel_ids))
@not_if_readonly
@web.authenticated
def post(self):
km = self.application.kernel_manager
@ -138,6 +152,7 @@ class KernelHandler(AuthenticatedHandler):
SUPPORTED_METHODS = ('DELETE')
@not_if_readonly
@web.authenticated
def delete(self, kernel_id):
km = self.application.kernel_manager
@ -148,6 +163,7 @@ class KernelHandler(AuthenticatedHandler):
class KernelActionHandler(AuthenticatedHandler):
@not_if_readonly
@web.authenticated
def post(self, kernel_id, action):
km = self.application.kernel_manager
@ -226,6 +242,7 @@ class AuthenticatedZMQStreamHandler(ZMQStreamHandler):
except:
logging.warn("couldn't parse cookie string: %s",msg, exc_info=True)
@not_if_readonly
def on_first_message(self, msg):
self._inject_cookie_message(msg)
if self.get_current_user() is None:
@ -369,6 +386,7 @@ class NotebookRootHandler(AuthenticatedHandler):
files = nbm.list_notebooks()
self.finish(jsonapi.dumps(files))
@not_if_readonly
@web.authenticated
def post(self):
nbm = self.application.notebook_manager
@ -401,6 +419,7 @@ class NotebookHandler(AuthenticatedHandler):
self.set_header('Last-Modified', last_mod)
self.finish(data)
@not_if_readonly
@web.authenticated
def put(self, notebook_id):
nbm = self.application.notebook_manager
@ -410,6 +429,7 @@ class NotebookHandler(AuthenticatedHandler):
self.set_status(204)
self.finish()
@not_if_readonly
@web.authenticated
def delete(self, notebook_id):
nbm = self.application.notebook_manager

@ -116,6 +116,10 @@ flags['no-browser']=(
{'NotebookApp' : {'open_browser' : False}},
"Don't open the notebook in a browser after startup."
)
flags['read-only'] = (
{'NotebookApp' : {'read_only' : True}},
"Launch the Notebook server in read-only mode, not allowing execution or editing"
)
# the flags that are specific to the frontend
# these must be scrubbed before being passed to the kernel,
@ -203,6 +207,10 @@ class NotebookApp(BaseIPythonApplication):
open_browser = Bool(True, config=True,
help="Whether to open in a browser after starting.")
read_only = Bool(False, config=True,
help="Whether to prevent editing/execution of notebooks."
)
def get_ws_url(self):
"""Return the WebSocket URL for this server."""
@ -282,7 +290,7 @@ class NotebookApp(BaseIPythonApplication):
# Try random ports centered around the default.
from random import randint
n = 50 # Max number of attempts, keep reasonably large.
for port in [self.port] + [self.port + randint(-2*n, 2*n) for i in range(n)]:
for port in range(self.port, self.port+5) + [self.port + randint(-2*n, 2*n) for i in range(n-5)]:
try:
self.http_server.listen(port, self.ip)
except socket.error, e:

Loading…
Cancel
Save