Merge *base_prefix config keys into base_prefix (fixes #996)

This commit is contained in:
Guillaume Ayoub 2013-04-30 14:02:17 +02:00
parent eaa969a05c
commit bf8e874e4d
6 changed files with 39 additions and 40 deletions

6
config
View File

@ -27,10 +27,8 @@ certificate = /etc/apache2/ssl/server.crt
key = /etc/apache2/ssl/server.key key = /etc/apache2/ssl/server.key
# Reverse DNS to resolve client address in logs # Reverse DNS to resolve client address in logs
dns_lookup = True dns_lookup = True
# base URL if / is not the CalDAV root. If set, must start with / # Root URL of Radicale (starting and ending with a slash)
base_prefix = base_prefix = /
# base URL if Radicale is running with / as CalDAV root, but in a subdir behind a proxy (like nginx). If set, must start with /
proxy_base_prefix =
[encoding] [encoding]

View File

@ -246,9 +246,17 @@ class Application(object):
headers = pprint.pformat(self.headers_log(environ)) headers = pprint.pformat(self.headers_log(environ))
log.LOGGER.debug("Request headers:\n%s" % headers) log.LOGGER.debug("Request headers:\n%s" % headers)
base_prefix = config.get("server", "base_prefix")
if environ["PATH_INFO"].startswith(base_prefix):
# Sanitize request URI # Sanitize request URI
environ["PATH_INFO"] = self.sanitize_uri(environ["PATH_INFO"]) environ["PATH_INFO"] = self.sanitize_uri(
"/%s" % environ["PATH_INFO"][len(base_prefix):])
log.LOGGER.debug("Sanitized path: %s", environ["PATH_INFO"]) log.LOGGER.debug("Sanitized path: %s", environ["PATH_INFO"])
else:
# Request path not starting with base_prefix, not allowed
log.LOGGER.debug(
"Path not starting with prefix: %s", environ["PATH_INFO"])
environ["PATH_INFO"] = None
# Get content # Get content
content_length = int(environ.get("CONTENT_LENGTH") or 0) content_length = int(environ.get("CONTENT_LENGTH") or 0)

View File

@ -146,6 +146,9 @@ def run():
finally: finally:
shutdown_program.set() shutdown_program.set()
log.LOGGER.debug(
"Base URL prefix: %s" % config.get("server", "base_prefix"))
# Start the servers in a different loop to avoid possible race-conditions, # Start the servers in a different loop to avoid possible race-conditions,
# when a server exists but another server is added to the list at the same # when a server exists but another server is added to the list at the same
# time # time

View File

@ -46,8 +46,7 @@ INITIAL_CONFIG = {
"certificate": "/etc/apache2/ssl/server.crt", "certificate": "/etc/apache2/ssl/server.crt",
"key": "/etc/apache2/ssl/server.key", "key": "/etc/apache2/ssl/server.key",
"dns_lookup": "True", "dns_lookup": "True",
"base_prefix": "", "base_prefix": "/"},
"proxy_base_prefix": ""},
"encoding": { "encoding": {
"request": "utf-8", "request": "utf-8",
"stock": "utf-8"}, "stock": "utf-8"},

View File

@ -203,6 +203,10 @@ class Collection(object):
The ``path`` is relative. The ``path`` is relative.
""" """
# path == None means wrong URL
if path is None:
return []
# First do normpath and then strip, to prevent access to FOLDER/../ # First do normpath and then strip, to prevent access to FOLDER/../
sane_path = posixpath.normpath(path.replace(os.sep, "/")).strip("/") sane_path = posixpath.normpath(path.replace(os.sep, "/")).strip("/")
attributes = sane_path.split("/") attributes = sane_path.split("/")
@ -490,7 +494,7 @@ class Collection(object):
@property @property
def url(self): def url(self):
"""Get the standard collection URL.""" """Get the standard collection URL."""
return "/%s/" % self.path return "%s/" % self.path
@property @property
def version(self): def version(self):

View File

@ -119,9 +119,9 @@ def _response(code):
return "HTTP/1.1 %i %s" % (code, client.responses[code]) return "HTTP/1.1 %i %s" % (code, client.responses[code])
def _href_with_proxy_base_prefix(href): def _href(href):
href = "%s%s" % (config.get("server", "proxy_base_prefix"), href) """Return prefixed href."""
return href.replace("//", "/") return "%s%s" % (config.get("server", "base_prefix"), href.lstrip("/"))
def name_from_path(path, collection): def name_from_path(path, collection):
@ -179,7 +179,7 @@ def delete(path, collection):
multistatus.append(response) multistatus.append(response)
href = ET.Element(_tag("D", "href")) href = ET.Element(_tag("D", "href"))
href.text = _href_with_proxy_base_prefix(path) href.text = _href(path)
response.append(href) response.append(href)
status = ET.Element(_tag("D", "status")) status = ET.Element(_tag("D", "status"))
@ -231,11 +231,8 @@ def _propfind_response(path, item, props, user):
response = ET.Element(_tag("D", "response")) response = ET.Element(_tag("D", "response"))
href = ET.Element(_tag("D", "href")) href = ET.Element(_tag("D", "href"))
if is_collection: uri = item.url if is_collection else "%s/%s" % (path, item.name)
uri = "%s%s" % (config.get("server", "base_prefix"), item.url) href.text = _href(uri.replace("//", "/"))
else:
uri = "%s/%s" % (path, item.name)
href.text = _href_with_proxy_base_prefix(uri)
response.append(href) response.append(href)
propstat404 = ET.Element(_tag("D", "propstat")) propstat404 = ET.Element(_tag("D", "propstat"))
@ -255,14 +252,14 @@ def _propfind_response(path, item, props, user):
element.text = item.etag element.text = item.etag
elif tag == _tag("D", "principal-URL"): elif tag == _tag("D", "principal-URL"):
tag = ET.Element(_tag("D", "href")) tag = ET.Element(_tag("D", "href"))
tag.text = _href_with_proxy_base_prefix(path) tag.text = _href(path)
element.append(tag) element.append(tag)
elif tag in (_tag("D", "principal-collection-set"), elif tag in (_tag("D", "principal-collection-set"),
_tag("C", "calendar-user-address-set"), _tag("C", "calendar-user-address-set"),
_tag("CR", "addressbook-home-set"), _tag("CR", "addressbook-home-set"),
_tag("C", "calendar-home-set")): _tag("C", "calendar-home-set")):
tag = ET.Element(_tag("D", "href")) tag = ET.Element(_tag("D", "href"))
tag.text = _href_with_proxy_base_prefix(path) tag.text = _href(path)
element.append(tag) element.append(tag)
elif tag == _tag("C", "supported-calendar-component-set"): elif tag == _tag("C", "supported-calendar-component-set"):
# This is not a Todo # This is not a Todo
@ -274,8 +271,7 @@ def _propfind_response(path, item, props, user):
# pylint: enable=W0511 # pylint: enable=W0511
elif tag == _tag("D", "current-user-principal") and user: elif tag == _tag("D", "current-user-principal") and user:
tag = ET.Element(_tag("D", "href")) tag = ET.Element(_tag("D", "href"))
prefixed_path = "%s/%s/" % (config.get("server", "base_prefix"), user) tag.text = _href("/%s/" % user)
tag.text = _href_with_proxy_base_prefix(prefixed_path)
element.append(tag) element.append(tag)
elif tag == _tag("D", "current-user-privilege-set"): elif tag == _tag("D", "current-user-privilege-set"):
privilege = ET.Element(_tag("D", "privilege")) privilege = ET.Element(_tag("D", "privilege"))
@ -399,7 +395,7 @@ def proppatch(path, xml_request, collection):
multistatus.append(response) multistatus.append(response)
href = ET.Element(_tag("D", "href")) href = ET.Element(_tag("D", "href"))
href.text = _href_with_proxy_base_prefix(path) href.text = _href(path)
response.append(href) response.append(href)
with collection.props as collection_props: with collection.props as collection_props:
@ -442,23 +438,15 @@ def report(path, xml_request, collection):
prop_element = root.find(_tag("D", "prop")) prop_element = root.find(_tag("D", "prop"))
props = [prop.tag for prop in prop_element] props = [prop.tag for prop in prop_element]
proxy_prefix = config.get("server", "proxy_base_prefix")
base_prefix = config.get("server", "base_prefix")
if collection: if collection:
if root.tag in (_tag("C", "calendar-multiget"), if root.tag in (_tag("C", "calendar-multiget"),
_tag("CR", "addressbook-multiget")): _tag("CR", "addressbook-multiget")):
# Read rfc4791-7.9 for info # Read rfc4791-7.9 for info
hreferences = set() base_prefix = config.get("server", "base_prefix")
for href_element in root.findall(_tag("D", "href")): hreferences = set(
# skip elements that don't have the correct base prefixes href_element.text[len(base_prefix):] for href_element
if not href_element.text.startswith(proxy_prefix): in root.findall(_tag("D", "href"))
continue if href_element.text.startswith(base_prefix))
unprefixed = href_element.text[len(proxy_prefix):]
if not unprefixed.startswith(base_prefix):
continue
# we keep the base prefix here, to be aligned with other paths
hreferences.add(unprefixed)
else: else:
hreferences = (path,) hreferences = (path,)
# TODO: handle other filters # TODO: handle other filters
@ -480,9 +468,8 @@ def report(path, xml_request, collection):
collection_timezones = collection.timezones collection_timezones = collection.timezones
for hreference in hreferences: for hreference in hreferences:
unprefixed_hreference = hreference[len(base_prefix):]
# Check if the reference is an item or a collection # Check if the reference is an item or a collection
name = name_from_path(unprefixed_hreference, collection) name = name_from_path(hreference, collection)
if name: if name:
# Reference is an item # Reference is an item
path = "/".join(hreference.split("/")[:-1]) + "/" path = "/".join(hreference.split("/")[:-1]) + "/"
@ -500,7 +487,7 @@ def report(path, xml_request, collection):
multistatus.append(response) multistatus.append(response)
href = ET.Element(_tag("D", "href")) href = ET.Element(_tag("D", "href"))
href.text = _href_with_proxy_base_prefix("%s/%s" % (path.rstrip("/"), item.name)) href.text = _href("%s/%s" % (path.rstrip("/"), item.name))
response.append(href) response.append(href)
propstat = ET.Element(_tag("D", "propstat")) propstat = ET.Element(_tag("D", "propstat"))