From 87d46d079db2159bcdc4a048c6277ff5875ed540 Mon Sep 17 00:00:00 2001 From: Andy Kittner Date: Tue, 12 Nov 2013 23:01:37 +0100 Subject: [PATCH] Rework DjangoStaticFileHandler (fixes #1034) Fixes multiple issues - Unicode errors when path contained non-ascii characters - 403 errors when the current working directory was not a parent directory of the static files - "API abuse" super() implementation of the get() method was passed a filesystem path instead of the url-path it was expecting - The above could also lead to 403 errors on case-insensitive file systems due to the way the base-class resolved and validated paths with respect to self.root --- openslides/utils/tornado_webserver.py | 29 +++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/openslides/utils/tornado_webserver.py b/openslides/utils/tornado_webserver.py index f8297c305..6b64df3b0 100644 --- a/openslides/utils/tornado_webserver.py +++ b/openslides/utils/tornado_webserver.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import os import posixpath from urllib import unquote @@ -10,7 +11,12 @@ from sockjs.tornado import SockJSRouter, SockJSConnection from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop from tornado.options import parse_command_line -from tornado.web import Application, FallbackHandler, StaticFileHandler +from tornado.web import ( + Application, + FallbackHandler, + StaticFileHandler, + HTTPError +) from tornado.wsgi import WSGIContainer @@ -19,14 +25,29 @@ class DjangoStaticFileHandler(StaticFileHandler): def initialize(self): """Overwrite some attributes.""" - self.root = '' + # NOTE: root is never actually used and default_filename is not + # supported (must always be None) + self.root = u'' self.default_filename = None - def get(self, path, include_body=True): + @classmethod + def get_absolute_path(cls, root, path): from django.contrib.staticfiles import finders normalized_path = posixpath.normpath(unquote(path)).lstrip('/') absolute_path = finders.find(normalized_path) - return super(DjangoStaticFileHandler, self).get(absolute_path, include_body) + return absolute_path + + def validate_absolute_path(self, root, absolute_path): + # differences from base implementation: + # - we ignore self.root since our files do not necessarily have + # a shared root prefix + # - we do not handle self.default_filename (we do not use it and it + # does not make much sense here anyway) + if not os.path.exists(absolute_path): + raise HTTPError(404) + if not os.path.isfile(absolute_path): + raise HTTPError(403, "%s is not a file", self.path) + return absolute_path class ProjectorSocketHandler(SockJSConnection):