From 7222bd53ad089a65fd610fab4626f9d0ab47dfce 2015-06-22 00:01:08 From: Min RK Date: 2015-06-22 00:01:08 Subject: [PATCH] Merge branch csp into 3.x Add APIHandler --- diff --git a/IPython/html/base/handlers.py b/IPython/html/base/handlers.py index 202bf36..d6c7b8b 100644 --- a/IPython/html/base/handlers.py +++ b/IPython/html/base/handlers.py @@ -42,16 +42,24 @@ sys_info = json.dumps(get_sys_info()) class AuthenticatedHandler(web.RequestHandler): """A RequestHandler with an authenticated user.""" + + @property + def content_security_policy(self): + """The default Content-Security-Policy header + + Can be overridden by defining Content-Security-Policy in settings['headers'] + """ + return '; '.join([ + "frame-ancestors 'self'", + # Make sure the report-uri is relative to the base_url + "report-uri " + url_path_join(self.base_url, csp_report_uri), + ]) def set_default_headers(self): headers = self.settings.get('headers', {}) if "Content-Security-Policy" not in headers: - headers["Content-Security-Policy"] = ( - "frame-ancestors 'self'; " - # Make sure the report-uri is relative to the base_url - "report-uri " + url_path_join(self.base_url, csp_report_uri) + ";" - ) + headers["Content-Security-Policy"] = self.content_security_policy # Allow for overriding headers for header_name,value in headers.items() : @@ -307,7 +315,22 @@ class IPythonHandler(AuthenticatedHandler): html = self.render_template('error.html', **ns) self.write(html) - + + +class APIHandler(IPythonHandler): + """Base class for API handlers""" + + @property + def content_security_policy(self): + csp = '; '.join([ + super(APIHandler, self).content_security_policy, + "default-src 'none'", + ]) + return csp + + def finish(self, *args, **kwargs): + self.set_header('Content-Type', 'application/json') + return super(APIHandler, self).finish(*args, **kwargs) class Template404(IPythonHandler): @@ -370,6 +393,7 @@ def json_errors(method): try: result = yield gen.maybe_future(method(self, *args, **kwargs)) except web.HTTPError as e: + self.set_header('Content-Type', 'application/json') status = e.status_code message = e.log_message self.log.warn(message) @@ -377,6 +401,7 @@ def json_errors(method): reply = dict(message=message, reason=e.reason) self.finish(json.dumps(reply)) except Exception: + self.set_header('Content-Type', 'application/json') self.log.error("Unhandled error in API request", exc_info=True) status = 500 message = "Unknown server error" @@ -399,7 +424,7 @@ def json_errors(method): # to minimize subclass changes: HTTPError = web.HTTPError -class FileFindHandler(web.StaticFileHandler): +class FileFindHandler(IPythonHandler, web.StaticFileHandler): """subclass of StaticFileHandler for serving files from a search path""" # cache search results, don't search for files more than once @@ -453,7 +478,7 @@ class FileFindHandler(web.StaticFileHandler): return super(FileFindHandler, self).validate_absolute_path(root, absolute_path) -class ApiVersionHandler(IPythonHandler): +class APIVersionHandler(APIHandler): @json_errors def get(self): @@ -524,5 +549,5 @@ path_regex = r"(?P(?:(?:/[^/]+)+|/?))" default_handlers = [ (r".*/", TrailingSlashHandler), - (r"api", ApiVersionHandler) + (r"api", APIVersionHandler) ] diff --git a/IPython/html/services/clusters/handlers.py b/IPython/html/services/clusters/handlers.py index a6d6312..a16d21f 100644 --- a/IPython/html/services/clusters/handlers.py +++ b/IPython/html/services/clusters/handlers.py @@ -7,28 +7,28 @@ import json from tornado import web -from ...base.handlers import IPythonHandler +from ...base.handlers import APIHandler #----------------------------------------------------------------------------- # Cluster handlers #----------------------------------------------------------------------------- -class MainClusterHandler(IPythonHandler): +class MainClusterHandler(APIHandler): @web.authenticated def get(self): self.finish(json.dumps(self.cluster_manager.list_profiles())) -class ClusterProfileHandler(IPythonHandler): +class ClusterProfileHandler(APIHandler): @web.authenticated def get(self, profile): self.finish(json.dumps(self.cluster_manager.profile_info(profile))) -class ClusterActionHandler(IPythonHandler): +class ClusterActionHandler(APIHandler): @web.authenticated def post(self, profile, action): diff --git a/IPython/html/services/config/handlers.py b/IPython/html/services/config/handlers.py index a7ff896..ae7f7e4 100644 --- a/IPython/html/services/config/handlers.py +++ b/IPython/html/services/config/handlers.py @@ -9,9 +9,9 @@ import errno from tornado import web from IPython.utils.py3compat import PY3 -from ...base.handlers import IPythonHandler, json_errors +from ...base.handlers import APIHandler, json_errors -class ConfigHandler(IPythonHandler): +class ConfigHandler(APIHandler): SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH') @web.authenticated diff --git a/IPython/html/services/contents/handlers.py b/IPython/html/services/contents/handlers.py index 1e8bd33..5cd849e 100644 --- a/IPython/html/services/contents/handlers.py +++ b/IPython/html/services/contents/handlers.py @@ -11,7 +11,7 @@ from IPython.html.utils import url_path_join, url_escape from IPython.utils.jsonutil import date_default from IPython.html.base.handlers import ( - IPythonHandler, json_errors, path_regex, + IPythonHandler, APIHandler, json_errors, path_regex, ) @@ -75,7 +75,7 @@ def validate_model(model, expect_content): ) -class ContentsHandler(IPythonHandler): +class ContentsHandler(APIHandler): SUPPORTED_METHODS = (u'GET', u'PUT', u'PATCH', u'POST', u'DELETE') @@ -257,7 +257,7 @@ class ContentsHandler(IPythonHandler): self.finish() -class CheckpointsHandler(IPythonHandler): +class CheckpointsHandler(APIHandler): SUPPORTED_METHODS = ('GET', 'POST') @@ -286,7 +286,7 @@ class CheckpointsHandler(IPythonHandler): self.finish(data) -class ModifyCheckpointsHandler(IPythonHandler): +class ModifyCheckpointsHandler(APIHandler): SUPPORTED_METHODS = ('POST', 'DELETE') diff --git a/IPython/html/services/kernels/handlers.py b/IPython/html/services/kernels/handlers.py index 14390ac..9be6845 100644 --- a/IPython/html/services/kernels/handlers.py +++ b/IPython/html/services/kernels/handlers.py @@ -13,12 +13,12 @@ from IPython.utils.jsonutil import date_default from IPython.utils.py3compat import cast_unicode from IPython.html.utils import url_path_join, url_escape -from ...base.handlers import IPythonHandler, json_errors +from ...base.handlers import IPythonHandler, APIHandler, json_errors from ...base.zmqhandlers import AuthenticatedZMQStreamHandler, deserialize_binary_message from IPython.core.release import kernel_protocol_version -class MainKernelHandler(IPythonHandler): +class MainKernelHandler(APIHandler): @web.authenticated @json_errors @@ -46,7 +46,7 @@ class MainKernelHandler(IPythonHandler): self.finish(json.dumps(model)) -class KernelHandler(IPythonHandler): +class KernelHandler(APIHandler): SUPPORTED_METHODS = ('DELETE', 'GET') @@ -67,7 +67,7 @@ class KernelHandler(IPythonHandler): self.finish() -class KernelActionHandler(IPythonHandler): +class KernelActionHandler(APIHandler): @web.authenticated @json_errors diff --git a/IPython/html/services/kernels/tests/test_kernels_api.py b/IPython/html/services/kernels/tests/test_kernels_api.py index b33142c..b779786 100644 --- a/IPython/html/services/kernels/tests/test_kernels_api.py +++ b/IPython/html/services/kernels/tests/test_kernels_api.py @@ -67,7 +67,8 @@ class KernelAPITest(NotebookTestBase): self.assertEqual(r.headers['Content-Security-Policy'], ( "frame-ancestors 'self'; " - "report-uri /api/security/csp-report;" + "report-uri /api/security/csp-report; " + "default-src 'none'" )) def test_main_kernel_handler(self): @@ -80,7 +81,8 @@ class KernelAPITest(NotebookTestBase): self.assertEqual(r.headers['Content-Security-Policy'], ( "frame-ancestors 'self'; " - "report-uri /api/security/csp-report;" + "report-uri /api/security/csp-report; " + "default-src 'none'" )) # GET request diff --git a/IPython/html/services/kernelspecs/handlers.py b/IPython/html/services/kernelspecs/handlers.py index 7239778..b826927 100644 --- a/IPython/html/services/kernelspecs/handlers.py +++ b/IPython/html/services/kernelspecs/handlers.py @@ -10,7 +10,7 @@ pjoin = os.path.join from tornado import web -from ...base.handlers import IPythonHandler, json_errors +from ...base.handlers import APIHandler, json_errors from ...utils import url_path_join def kernelspec_model(handler, name): @@ -40,7 +40,7 @@ def kernelspec_model(handler, name): ) return d -class MainKernelSpecHandler(IPythonHandler): +class MainKernelSpecHandler(APIHandler): SUPPORTED_METHODS = ('GET',) @web.authenticated @@ -62,7 +62,7 @@ class MainKernelSpecHandler(IPythonHandler): self.finish(json.dumps(model)) -class KernelSpecHandler(IPythonHandler): +class KernelSpecHandler(APIHandler): SUPPORTED_METHODS = ('GET',) @web.authenticated diff --git a/IPython/html/services/nbconvert/handlers.py b/IPython/html/services/nbconvert/handlers.py index 1c74de5..d6e9e0d 100644 --- a/IPython/html/services/nbconvert/handlers.py +++ b/IPython/html/services/nbconvert/handlers.py @@ -2,9 +2,9 @@ import json from tornado import web -from ...base.handlers import IPythonHandler, json_errors +from ...base.handlers import APIHandler, json_errors -class NbconvertRootHandler(IPythonHandler): +class NbconvertRootHandler(APIHandler): SUPPORTED_METHODS = ('GET',) @web.authenticated diff --git a/IPython/html/services/security/handlers.py b/IPython/html/services/security/handlers.py index 18f7874..03a0dcf 100644 --- a/IPython/html/services/security/handlers.py +++ b/IPython/html/services/security/handlers.py @@ -5,10 +5,10 @@ from tornado import gen, web -from ...base.handlers import IPythonHandler, json_errors +from ...base.handlers import APIHandler, json_errors from . import csp_report_uri -class CSPReportHandler(IPythonHandler): +class CSPReportHandler(APIHandler): '''Accepts a content security policy violation report''' @web.authenticated @json_errors diff --git a/IPython/html/services/sessions/handlers.py b/IPython/html/services/sessions/handlers.py index 9d0a5e4..36c5160 100644 --- a/IPython/html/services/sessions/handlers.py +++ b/IPython/html/services/sessions/handlers.py @@ -7,13 +7,13 @@ import json from tornado import web -from ...base.handlers import IPythonHandler, json_errors +from ...base.handlers import APIHandler, json_errors from IPython.utils.jsonutil import date_default from IPython.html.utils import url_path_join, url_escape from IPython.kernel.kernelspec import NoSuchKernel -class SessionRootHandler(IPythonHandler): +class SessionRootHandler(APIHandler): @web.authenticated @json_errors @@ -65,7 +65,7 @@ class SessionRootHandler(IPythonHandler): self.set_status(201) self.finish(json.dumps(model, default=date_default)) -class SessionHandler(IPythonHandler): +class SessionHandler(APIHandler): SUPPORTED_METHODS = ('GET', 'PATCH', 'DELETE') diff --git a/IPython/html/terminal/api_handlers.py b/IPython/html/terminal/api_handlers.py index 6895e71..a7be0ae 100644 --- a/IPython/html/terminal/api_handlers.py +++ b/IPython/html/terminal/api_handlers.py @@ -1,9 +1,9 @@ import json from tornado import web, gen -from ..base.handlers import IPythonHandler, json_errors +from ..base.handlers import APIHandler, json_errors from ..utils import url_path_join -class TerminalRootHandler(IPythonHandler): +class TerminalRootHandler(APIHandler): @web.authenticated @json_errors def get(self): @@ -19,7 +19,7 @@ class TerminalRootHandler(IPythonHandler): self.finish(json.dumps({'name': name})) -class TerminalHandler(IPythonHandler): +class TerminalHandler(APIHandler): SUPPORTED_METHODS = ('GET', 'DELETE') @web.authenticated