Improve rights checking and request handlers
* Access rights are checked before the storage is locked and collections are loaded. * DELETE sends 410 instead of doing nothing or crashing if the target doesn't exist. * GET always returns 404 if the target doesn't exist. * GET doesn't crash if a collection without tag property is requested. * MKCOL and MKCALENDAR send 409 if the target already exists. * MOVE checks if the target collection of an item actually exists and sends 409 otherwise. * PUT doesn't crash if a whole collection that doesn't exist yet is uploaded and ``content-type`` is ``text/vcard`` or ``text/calendar``. * PUT distinguishes between simple items and whole collections by the following criteria: Target is a collection; Parent exists; Parent has the tag property set; Parent contains other items. Before only the first two criteria where used, which was very unrelieable. #384 * PROPPATCH is only allowed on collections and 409 is send otherwise. * ``Rights.authorized`` takes a path instead of a collection. * ``Collection.discover`` only returns items in ``path``, that actually exist. #442
This commit is contained in:
@ -107,19 +107,17 @@ class Rights(BaseRights):
|
||||
self.filename = os.path.expanduser(configuration.get("rights", "file"))
|
||||
self.rights_type = configuration.get("rights", "type").lower()
|
||||
|
||||
def authorized(self, user, collection, permission):
|
||||
def authorized(self, user, path, permission):
|
||||
user = user or ""
|
||||
if user and not storage.is_safe_path_component(user):
|
||||
# Prevent usernames like "user/calendar.ics"
|
||||
raise ValueError("Unsafe username")
|
||||
collection_url = collection.path.rstrip("/")
|
||||
if collection_url in (".well-known/carddav", ".well-known/caldav"):
|
||||
return permission == "r"
|
||||
sane_path = storage.sanitize_path(path).strip("/")
|
||||
# Prevent "regex injection"
|
||||
user_escaped = re.escape(user)
|
||||
collection_url_escaped = re.escape(collection_url)
|
||||
sane_path_escaped = re.escape(sane_path)
|
||||
regex = ConfigParser(
|
||||
{"login": user_escaped, "path": collection_url_escaped})
|
||||
{"login": user_escaped, "path": sane_path_escaped})
|
||||
if self.rights_type in DEFINED_RIGHTS:
|
||||
self.logger.debug("Rights type '%s'" % self.rights_type)
|
||||
regex.readfp(StringIO(DEFINED_RIGHTS[self.rights_type]))
|
||||
@ -135,11 +133,11 @@ class Rights(BaseRights):
|
||||
re_collection = regex.get(section, "collection")
|
||||
self.logger.debug(
|
||||
"Test if '%s:%s' matches against '%s:%s' from section '%s'" % (
|
||||
user, collection_url, re_user, re_collection, section))
|
||||
user, sane_path, re_user, re_collection, section))
|
||||
user_match = re.fullmatch(re_user, user)
|
||||
if user_match:
|
||||
re_collection = re_collection.format(*user_match.groups())
|
||||
if re.fullmatch(re_collection, collection_url):
|
||||
if re.fullmatch(re_collection, sane_path):
|
||||
self.logger.debug("Section '%s' matches" % section)
|
||||
return permission in regex.get(section, "permission")
|
||||
else:
|
||||
|
Reference in New Issue
Block a user