From 912e05bda203346e76de397ed5c8a02c86739ef5 Mon Sep 17 00:00:00 2001 From: Zsailer Date: Fri, 10 Jan 2020 11:09:27 -0800 Subject: [PATCH] update authorized wrapper with resource --- jupyter_server/files/handlers.py | 4 ++-- jupyter_server/kernelspecs/handlers.py | 4 ++-- jupyter_server/nbconvert/handlers.py | 5 ++++ jupyter_server/services/api/handlers.py | 4 +++- jupyter_server/services/config/handlers.py | 8 +++---- jupyter_server/services/contents/handlers.py | 23 +++++++++---------- jupyter_server/services/kernels/handlers.py | 10 ++++---- .../services/kernelspecs/handlers.py | 5 ++-- jupyter_server/services/nbconvert/handlers.py | 2 ++ jupyter_server/services/security/handlers.py | 2 ++ jupyter_server/services/sessions/handlers.py | 11 ++++----- jupyter_server/services/shutdown.py | 2 ++ jupyter_server/terminal/api_handlers.py | 5 ++++ jupyter_server/utils.py | 2 -- jupyter_server/view/handlers.py | 3 ++- 15 files changed, 52 insertions(+), 38 deletions(-) diff --git a/jupyter_server/files/handlers.py b/jupyter_server/files/handlers.py index 1717a2c858..41c0fb05c7 100644 --- a/jupyter_server/files/handlers.py +++ b/jupyter_server/files/handlers.py @@ -28,14 +28,14 @@ def content_security_policy(self): "; sandbox allow-scripts" @web.authenticated - @authorized('read') + @authorized("read", resource="files") def head(self, path): self.get(path, include_body=False) self.check_xsrf_cookie() return self.get(path, include_body=False) @web.authenticated - @authorized('read') + @authorized("read", resource="files") async def get(self, path, include_body=True): # /files/ requests must originate from the same site self.check_xsrf_cookie() diff --git a/jupyter_server/kernelspecs/handlers.py b/jupyter_server/kernelspecs/handlers.py index a5f5d7cf05..2e0e2a2922 100644 --- a/jupyter_server/kernelspecs/handlers.py +++ b/jupyter_server/kernelspecs/handlers.py @@ -11,7 +11,7 @@ def initialize(self): web.StaticFileHandler.initialize(self, path='') @web.authenticated - @authorized("read") + @authorized("read", resource="kernelspecs") def get(self, kernel_name, path, include_body=True): ksm = self.kernel_spec_manager try: @@ -23,7 +23,7 @@ def get(self, kernel_name, path, include_body=True): return web.StaticFileHandler.get(self, path, include_body=include_body) @web.authenticated - @authorized("read") + @authorized("read", resource="kernelspecs") def head(self, kernel_name, path): return self.get(kernel_name, path, include_body=False) diff --git a/jupyter_server/nbconvert/handlers.py b/jupyter_server/nbconvert/handlers.py index b9cb711a68..e3c3ad5796 100644 --- a/jupyter_server/nbconvert/handlers.py +++ b/jupyter_server/nbconvert/handlers.py @@ -20,6 +20,9 @@ from ipython_genutils.py3compat import cast_bytes from ipython_genutils import text +from jupyter_server.utils import authorized + + def find_resource_files(output_files_dir): files = [] for dirpath, dirnames, filenames in os.walk(output_files_dir): @@ -81,6 +84,7 @@ class NbconvertFileHandler(JupyterHandler): SUPPORTED_METHODS = ('GET',) @web.authenticated + @authorized("read", resource="nbconvert") async def get(self, format, path): self.check_xsrf_cookie() exporter = get_exporter(format, config=self.config, log=self.log) @@ -150,6 +154,7 @@ class NbconvertPostHandler(JupyterHandler): SUPPORTED_METHODS = ('POST',) @web.authenticated + @authorized("write", resource="nbconvert") def post(self, format): exporter = get_exporter(format, config=self.config) diff --git a/jupyter_server/services/api/handlers.py b/jupyter_server/services/api/handlers.py index 571c643d51..25c2ffe3f4 100644 --- a/jupyter_server/services/api/handlers.py +++ b/jupyter_server/services/api/handlers.py @@ -9,7 +9,7 @@ from tornado import web from ...base.handlers import JupyterHandler, APIHandler -from jupyter_server.utils import ensure_async +from jupyter_server.utils import ensure_async, authorized from jupyter_server._tz import utcfromtimestamp, isoformat @@ -19,6 +19,7 @@ def initialize(self): web.StaticFileHandler.initialize(self, path=os.path.dirname(__file__)) @web.authenticated + @authorized("read", resource="api") def get(self): self.log.warning("Serving api spec (experimental, incomplete)") return web.StaticFileHandler.get(self, 'api.yaml') @@ -32,6 +33,7 @@ class APIStatusHandler(APIHandler): _track_activity = False @web.authenticated + @authorized("read", resource="api") async def get(self): # if started was missing, use unix epoch started = self.settings.get('started', utcfromtimestamp(0)) diff --git a/jupyter_server/services/config/handlers.py b/jupyter_server/services/config/handlers.py index f4918c3aa5..5c03d7109a 100644 --- a/jupyter_server/services/config/handlers.py +++ b/jupyter_server/services/config/handlers.py @@ -9,27 +9,25 @@ from tornado import web from ...base.handlers import APIHandler - from jupyter_server.utils import authorized - class ConfigHandler(APIHandler): @web.authenticated - @authorized('read') + @authorized("read", resource="config") def get(self, section_name): self.set_header("Content-Type", 'application/json') self.finish(json.dumps(self.config_manager.get(section_name))) @web.authenticated - @authorized('write') + @authorized("write", resource="config") def put(self, section_name): data = self.get_json_body() # Will raise 400 if content is not valid JSON self.config_manager.set(section_name, data) self.set_status(204) @web.authenticated - @authorized('write') + @authorized("write", resource="config") def patch(self, section_name): new_data = self.get_json_body() section = self.config_manager.update(section_name, new_data) diff --git a/jupyter_server/services/contents/handlers.py b/jupyter_server/services/contents/handlers.py index 6e149971e0..8736c275e3 100644 --- a/jupyter_server/services/contents/handlers.py +++ b/jupyter_server/services/contents/handlers.py @@ -16,9 +16,9 @@ from jupyter_server.base.handlers import ( JupyterHandler, APIHandler, path_regex, ) - from jupyter_server.utils import authorized + def validate_model(model, expect_content): """ Validate a model returned by a ContentsManager method. @@ -89,7 +89,7 @@ def _finish_model(self, model, location=True): self.finish(json.dumps(model, default=date_default)) @web.authenticated - @authorized('read', resource='contents') + @authorized("read", resource="contents") async def get(self, path=''): """Return a model for a file or directory. @@ -116,7 +116,7 @@ async def get(self, path=''): self._finish_model(model, location=False) @web.authenticated - @authorized('write', resource='contents') + @authorized("write", resource="contents") async def patch(self, path=''): """PATCH renames a file or directory without re-uploading content.""" cm = self.contents_manager @@ -165,7 +165,7 @@ async def _save(self, model, path): self._finish_model(model) @web.authenticated - @authorized('write', resource='contents') + @authorized("write", resource="contents") async def post(self, path=''): """Create a new file in the specified path. @@ -202,7 +202,7 @@ async def post(self, path=''): await self._new_untitled(path) @web.authenticated - @authorized('write', resource='contents') + @authorized("write", resource="contents") async def put(self, path=''): """Saves the file in the location specified by name and path. @@ -227,7 +227,7 @@ async def put(self, path=''): await self._new_untitled(path) @web.authenticated - @authorized('write', resource='contents') + @authorized("write", resource="contents") async def delete(self, path=''): """delete a file in the given path""" cm = self.contents_manager @@ -240,7 +240,7 @@ async def delete(self, path=''): class CheckpointsHandler(APIHandler): @web.authenticated - @authorized('read', resource='checkpoints') + @authorized("read", resource="contents") async def get(self, path=''): """get lists checkpoints for a file""" cm = self.contents_manager @@ -249,7 +249,7 @@ async def get(self, path=''): self.finish(data) @web.authenticated - @authorized('write', resource='checkpoints') + @authorized("write", resource="contents") async def post(self, path=''): """post creates a new checkpoint""" cm = self.contents_manager @@ -265,7 +265,7 @@ async def post(self, path=''): class ModifyCheckpointsHandler(APIHandler): @web.authenticated - @authorized('write', resource='checkpoints') + @authorized("write", resource="contents") async def post(self, path, checkpoint_id): """post restores a file from a checkpoint""" cm = self.contents_manager @@ -274,7 +274,7 @@ async def post(self, path, checkpoint_id): self.finish() @web.authenticated - @authorized('write', resource='checkpoints') + @authorized("write", resource="contents") async def delete(self, path, checkpoint_id): """delete clears a checkpoint for a given file""" cm = self.contents_manager @@ -302,13 +302,12 @@ class TrustNotebooksHandler(JupyterHandler): """ Handles trust/signing of notebooks """ @web.authenticated - @authorized('write', resource='trust_notebook') + @authorized("write", resource="contents") async def post(self,path=''): cm = self.contents_manager await ensure_async(cm.trust_notebook(path)) self.set_status(201) self.finish() - #----------------------------------------------------------------------------- # URL to handler mappings #----------------------------------------------------------------------------- diff --git a/jupyter_server/services/kernels/handlers.py b/jupyter_server/services/kernels/handlers.py index 57ac7b2b72..8c5802f6b5 100644 --- a/jupyter_server/services/kernels/handlers.py +++ b/jupyter_server/services/kernels/handlers.py @@ -27,14 +27,14 @@ class MainKernelHandler(APIHandler): @web.authenticated - @authorized('read', resource='kernels') + @authorized("read", resource="kernels") async def get(self): km = self.kernel_manager kernels = await ensure_async(km.list_kernels()) self.finish(json.dumps(kernels, default=date_default)) @web.authenticated - @authorized('write', resource='kernels') + @authorized("write", resource="kernels") async def post(self): km = self.kernel_manager model = self.get_json_body() @@ -57,14 +57,14 @@ async def post(self): class KernelHandler(APIHandler): @web.authenticated - @authorized('read', resource='kernels') + @authorized("read", resource="kernels") async def get(self, kernel_id): km = self.kernel_manager model = await ensure_async(km.kernel_model(kernel_id)) self.finish(json.dumps(model, default=date_default)) @web.authenticated - @authorized('write', resource='kernels') + @authorized("write", resource="kernels") async def delete(self, kernel_id): km = self.kernel_manager await ensure_async(km.shutdown_kernel(kernel_id)) @@ -75,7 +75,7 @@ async def delete(self, kernel_id): class KernelActionHandler(APIHandler): @web.authenticated - @authorized('write', resource='kernels') + @authorized("write", resource="kernels") async def post(self, kernel_id, action): km = self.kernel_manager if action == 'interrupt': diff --git a/jupyter_server/services/kernelspecs/handlers.py b/jupyter_server/services/kernelspecs/handlers.py index 19afda888c..3c93919430 100644 --- a/jupyter_server/services/kernelspecs/handlers.py +++ b/jupyter_server/services/kernelspecs/handlers.py @@ -18,6 +18,7 @@ from jupyter_server.utils import authorized + def kernelspec_model(handler, name, spec_dict, resource_dir): """Load a KernelSpec by name and return the REST API model""" d = { @@ -56,7 +57,7 @@ def is_kernelspec_model(spec_dict): class MainKernelSpecHandler(APIHandler): @web.authenticated - @authorized('read', resource='kernelspecs') + @authorized("read", resource="kernelspecs") async def get(self): ksm = self.kernel_spec_manager km = self.kernel_manager @@ -81,7 +82,7 @@ async def get(self): class KernelSpecHandler(APIHandler): @web.authenticated - @authorized('read', resource='kernelspecs') + @authorized("read", resource="kernelspecs") async def get(self, kernel_name): ksm = self.kernel_spec_manager kernel_name = url_unescape(kernel_name) diff --git a/jupyter_server/services/nbconvert/handlers.py b/jupyter_server/services/nbconvert/handlers.py index a15dd3e314..46b798535a 100644 --- a/jupyter_server/services/nbconvert/handlers.py +++ b/jupyter_server/services/nbconvert/handlers.py @@ -4,11 +4,13 @@ from tornado import web from ...base.handlers import APIHandler +from jupyter_server.utils import authorized class NbconvertRootHandler(APIHandler): @web.authenticated + @authorized("read", resource="nbconvert") async def get(self): try: from nbconvert.exporters import base diff --git a/jupyter_server/services/security/handlers.py b/jupyter_server/services/security/handlers.py index 82a00d234b..0539e8e7af 100644 --- a/jupyter_server/services/security/handlers.py +++ b/jupyter_server/services/security/handlers.py @@ -6,6 +6,7 @@ from tornado import web from ...base.handlers import APIHandler +from jupyter_server.utils import authorized from . import csp_report_uri class CSPReportHandler(APIHandler): @@ -22,6 +23,7 @@ def check_xsrf_cookie(self): return @web.authenticated + @authorized("write", resource="csp") def post(self): '''Log a content security policy violation report''' self.log.warning("Content security violation: %s", diff --git a/jupyter_server/services/sessions/handlers.py b/jupyter_server/services/sessions/handlers.py index dda6ea9892..927b930fdc 100644 --- a/jupyter_server/services/sessions/handlers.py +++ b/jupyter_server/services/sessions/handlers.py @@ -14,14 +14,13 @@ from jupyter_client.jsonutil import date_default from jupyter_server.utils import url_path_join, ensure_async from jupyter_client.kernelspec import NoSuchKernel - from jupyter_server.utils import authorized class SessionRootHandler(APIHandler): @web.authenticated - @authorized('read', resource='sessions') + @authorized("read", resource="sessions") async def get(self): # Return a list of running sessions sm = self.session_manager @@ -29,7 +28,7 @@ async def get(self): self.finish(json.dumps(sessions, default=date_default)) @web.authenticated - @authorized('write', resource='sessions') + @authorized("write", resource="sessions") async def post(self): # Creates a new session #(unless a session already exists for the named session) @@ -90,7 +89,7 @@ async def post(self): class SessionHandler(APIHandler): @web.authenticated - @authorized('read', resource='sessions') + @authorized("read", resource="sessions") async def get(self, session_id): # Returns the JSON model for a single session sm = self.session_manager @@ -98,7 +97,7 @@ async def get(self, session_id): self.finish(json.dumps(model, default=date_default)) @web.authenticated - @authorized('write', resource='sessions') + @authorized("write", resource="sessions") async def patch(self, session_id): """Patch updates sessions: @@ -149,7 +148,7 @@ async def patch(self, session_id): self.finish(json.dumps(model, default=date_default)) @web.authenticated - @authorized('write', resource='sessions') + @authorized("write", resource="sessions") async def delete(self, session_id): # Deletes the session with given session_id sm = self.session_manager diff --git a/jupyter_server/services/shutdown.py b/jupyter_server/services/shutdown.py index c7f5361c42..d62eb9237f 100644 --- a/jupyter_server/services/shutdown.py +++ b/jupyter_server/services/shutdown.py @@ -2,10 +2,12 @@ """ from tornado import web, ioloop from jupyter_server.base.handlers import JupyterHandler +from jupyter_server.utils import authorized class ShutdownHandler(JupyterHandler): @web.authenticated + @authorized("write", resource="shutdown") def post(self): self.log.info("Shutting down on /api/shutdown request.") ioloop.IOLoop.current().stop() diff --git a/jupyter_server/terminal/api_handlers.py b/jupyter_server/terminal/api_handlers.py index 92bb624289..6a6dd00789 100644 --- a/jupyter_server/terminal/api_handlers.py +++ b/jupyter_server/terminal/api_handlers.py @@ -1,16 +1,19 @@ import json from tornado import web from ..base.handlers import APIHandler +from jupyter_server.utils import authorized class TerminalRootHandler(APIHandler): @web.authenticated + @authorized("read", resource="terminal") def get(self): models = self.terminal_manager.list() self.finish(json.dumps(models)) @web.authenticated + @authorized("write", resource="terminal") def post(self): """POST /terminals creates a new terminal and redirects to it""" data = self.get_json_body() or {} @@ -23,11 +26,13 @@ class TerminalHandler(APIHandler): SUPPORTED_METHODS = ('GET', 'DELETE') @web.authenticated + @authorized("read", resource="terminal") def get(self, name): model = self.terminal_manager.get(name) self.finish(json.dumps(model)) @web.authenticated + @authorized("write", resource="terminal") async def delete(self, name): await self.terminal_manager.terminate(name, force=True) self.set_status(204) diff --git a/jupyter_server/utils.py b/jupyter_server/utils.py index b47c1b4d8b..fe8ae92863 100644 --- a/jupyter_server/utils.py +++ b/jupyter_server/utils.py @@ -9,8 +9,6 @@ import os import socket import sys -import functools - from distutils.version import LooseVersion from contextlib import contextmanager diff --git a/jupyter_server/view/handlers.py b/jupyter_server/view/handlers.py index 76f5a65b29..2508759ef0 100644 --- a/jupyter_server/view/handlers.py +++ b/jupyter_server/view/handlers.py @@ -6,12 +6,13 @@ from tornado import web from ..base.handlers import JupyterHandler, path_regex -from ..utils import ensure_async, url_escape, url_path_join +from ..utils import ensure_async, url_escape, url_path_join, authorized class ViewHandler(JupyterHandler): """Render HTML files within an iframe.""" @web.authenticated + @authorized("read", resource="view") async def get(self, path): path = path.strip('/') if not await ensure_async(self.contents_manager.file_exists(path)):