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
This commit is contained in:
Andy Kittner 2013-11-12 23:01:37 +01:00
parent e54faaf1e7
commit 87d46d079d

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os
import posixpath import posixpath
from urllib import unquote from urllib import unquote
@ -10,7 +11,12 @@ from sockjs.tornado import SockJSRouter, SockJSConnection
from tornado.httpserver import HTTPServer from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop from tornado.ioloop import IOLoop
from tornado.options import parse_command_line 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 from tornado.wsgi import WSGIContainer
@ -19,14 +25,29 @@ class DjangoStaticFileHandler(StaticFileHandler):
def initialize(self): def initialize(self):
"""Overwrite some attributes.""" """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 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 from django.contrib.staticfiles import finders
normalized_path = posixpath.normpath(unquote(path)).lstrip('/') normalized_path = posixpath.normpath(unquote(path)).lstrip('/')
absolute_path = finders.find(normalized_path) 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): class ProjectorSocketHandler(SockJSConnection):