sharing/propfind: bugfix related to backmap in case of listing

This commit is contained in:
Peter Bieringer
2026-03-14 12:40:30 +01:00
parent edc64b5ba7
commit aa5d59a48d

View File

@@ -25,8 +25,7 @@ import posixpath
import socket import socket
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from http import client from http import client
from typing import (Dict, Iterable, Iterator, List, Optional, Sequence, Tuple, from typing import Dict, Iterable, Iterator, List, Optional, Sequence, Tuple
Union)
from radicale import (httputils, pathutils, rights, storage, types, utils, from radicale import (httputils, pathutils, rights, storage, types, utils,
xmlutils) xmlutils)
@@ -37,7 +36,7 @@ from radicale.log import logger
def xml_propfind(base_prefix: str, path: str, def xml_propfind(base_prefix: str, path: str,
xml_request: Optional[ET.Element], xml_request: Optional[ET.Element],
allowed_items: Iterable[Tuple[types.CollectionOrItem, str]], allowed_items: Iterable[Tuple[types.CollectionOrItem, str]],
user: str, encoding: str, max_resource_size: int, share: Union[dict, None] = None) -> Optional[ET.Element]: user: str, encoding: str, max_resource_size: int, shares: dict = {}) -> Optional[ET.Element]:
"""Read and answer PROPFIND requests. """Read and answer PROPFIND requests.
Read rfc4918-9.1 for info. Read rfc4918-9.1 for info.
@@ -74,7 +73,7 @@ def xml_propfind(base_prefix: str, path: str,
write = permission == "w" write = permission == "w"
multistatus.append(xml_propfind_response( multistatus.append(xml_propfind_response(
base_prefix, path, item, props, user, encoding, write=write, base_prefix, path, item, props, user, encoding, write=write,
allprop=allprop, propname=propname, max_resource_size=max_resource_size, share=share)) allprop=allprop, propname=propname, max_resource_size=max_resource_size, shares=shares))
return multistatus return multistatus
@@ -82,7 +81,7 @@ def xml_propfind(base_prefix: str, path: str,
def xml_propfind_response( def xml_propfind_response(
base_prefix: str, path: str, item: types.CollectionOrItem, base_prefix: str, path: str, item: types.CollectionOrItem,
props: Sequence[str], user: str, encoding: str, max_resource_size: int, write: bool = False, props: Sequence[str], user: str, encoding: str, max_resource_size: int, write: bool = False,
propname: bool = False, allprop: bool = False, share: Union[dict, None] = None) -> ET.Element: propname: bool = False, allprop: bool = False, shares: dict = {}) -> ET.Element:
"""Build and return a PROPFIND response.""" """Build and return a PROPFIND response."""
if propname and allprop or (props and (propname or allprop)): if propname and allprop or (props and (propname or allprop)):
raise ValueError("Only use one of props, propname and allprops") raise ValueError("Only use one of props, propname and allprops")
@@ -102,6 +101,10 @@ def xml_propfind_response(
collection.path, item.href)) collection.path, item.href))
response = ET.Element(xmlutils.make_clark("D:response")) response = ET.Element(xmlutils.make_clark("D:response"))
href = ET.Element(xmlutils.make_clark("D:href")) href = ET.Element(xmlutils.make_clark("D:href"))
if uri in shares:
share = shares[uri]
else:
share = None
if share: if share:
# backmap # backmap
uri = uri.replace(share['PathMapped'], share['PathOrToken']) uri = uri.replace(share['PathMapped'], share['PathOrToken'])
@@ -436,7 +439,7 @@ class ApplicationPartPropfind(ApplicationBase):
"""Manage PROPFIND request.""" """Manage PROPFIND request."""
http_depth = environ.get("HTTP_DEPTH", "0") http_depth = environ.get("HTTP_DEPTH", "0")
permissions_filter = None permissions_filter = None
share = None shares: dict = {}
if self._sharing._enabled: if self._sharing._enabled:
# Sharing by token or map (if enabled) # Sharing by token or map (if enabled)
share = self._sharing.sharing_collection_resolver(path, user) share = self._sharing.sharing_collection_resolver(path, user)
@@ -445,6 +448,7 @@ class ApplicationPartPropfind(ApplicationBase):
path = share['PathMapped'] path = share['PathMapped']
user = share['Owner'] user = share['Owner']
permissions_filter = share['Permissions'] permissions_filter = share['Permissions']
shares[share['PathMapped']] = share
access = Access(self._rights, user, path, permissions_filter) access = Access(self._rights, user, path, permissions_filter)
if not access.check("r"): if not access.check("r"):
return httputils.NOT_ALLOWED return httputils.NOT_ALLOWED
@@ -497,10 +501,11 @@ class ApplicationPartPropfind(ApplicationBase):
c_items_iter = iter(self._storage.discover(c_path, "0")) c_items_iter = iter(self._storage.discover(c_path, "0"))
c_allowed_items = list(self._collect_allowed_items(c_items_iter, c_user)) c_allowed_items = list(self._collect_allowed_items(c_items_iter, c_user))
allowed_items = allowed_items + c_allowed_items allowed_items = allowed_items + c_allowed_items
shares[c_path] = share
headers = {"DAV": httputils.DAV_HEADERS, headers = {"DAV": httputils.DAV_HEADERS,
"Content-Type": "text/xml; charset=%s" % self._encoding} "Content-Type": "text/xml; charset=%s" % self._encoding}
xml_answer = xml_propfind(base_prefix, path, xml_content, xml_answer = xml_propfind(base_prefix, path, xml_content,
allowed_items, user, self._encoding, max_resource_size=self._max_resource_size, share=share) allowed_items, user, self._encoding, max_resource_size=self._max_resource_size, shares=shares)
if xml_answer is None: if xml_answer is None:
return httputils.NOT_ALLOWED return httputils.NOT_ALLOWED
return client.MULTI_STATUS, headers, self._xml_response(xml_answer), xmlutils.pretty_xml(xml_content) return client.MULTI_STATUS, headers, self._xml_response(xml_answer), xmlutils.pretty_xml(xml_content)