sharing/propfind: fix/reorg collection listing
This commit is contained in:
@@ -25,7 +25,8 @@ 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)
|
||||||
@@ -35,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, str]],
|
||||||
user: str, encoding: str, max_resource_size: int, shares: dict = {}) -> 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.
|
||||||
|
|
||||||
@@ -69,11 +70,14 @@ def xml_propfind(base_prefix: str, path: str,
|
|||||||
# Writing answer
|
# Writing answer
|
||||||
multistatus = ET.Element(xmlutils.make_clark("D:multistatus"))
|
multistatus = ET.Element(xmlutils.make_clark("D:multistatus"))
|
||||||
|
|
||||||
for item, permission in allowed_items:
|
if logger.isEnabledFor(logging.DEBUG):
|
||||||
|
logger.debug("TRACE/PROPFIND/xml_propfind: shares=%r", shares)
|
||||||
|
|
||||||
|
for item, permission, sharetype in allowed_items:
|
||||||
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, shares=shares))
|
allprop=allprop, propname=propname, max_resource_size=max_resource_size, shares=shares, sharetype=sharetype))
|
||||||
|
|
||||||
return multistatus
|
return multistatus
|
||||||
|
|
||||||
@@ -81,7 +85,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, shares: dict = {}) -> ET.Element:
|
propname: bool = False, allprop: bool = False, shares: dict = {}, sharetype: Union[str | None] = None) -> 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")
|
||||||
@@ -101,10 +105,22 @@ 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]
|
# lookup share
|
||||||
else:
|
share = None
|
||||||
share = None
|
if logger.isEnabledFor(logging.DEBUG):
|
||||||
|
logger.debug("TRACE/PROPFIND/xml_propfind: sharetype=%r item.path=%r", sharetype, uri)
|
||||||
|
for entry in shares:
|
||||||
|
if logger.isEnabledFor(logging.DEBUG):
|
||||||
|
logger.debug("TRACE/PROPFIND/xml_propfind: entry=%r", entry)
|
||||||
|
if entry is not None:
|
||||||
|
if shares[entry]['PathMapped'] == uri:
|
||||||
|
if sharetype is None or shares[entry]['ShareType'] == sharetype:
|
||||||
|
share = shares[entry]
|
||||||
|
if logger.isEnabledFor(logging.DEBUG):
|
||||||
|
logger.debug("TRACE/PROPFIND/xml_propfind: share=%r", share)
|
||||||
|
break
|
||||||
|
|
||||||
if share:
|
if share:
|
||||||
# backmap
|
# backmap
|
||||||
uri = uri.replace(share['PathMapped'], share['PathOrToken'])
|
uri = uri.replace(share['PathMapped'], share['PathOrToken'])
|
||||||
@@ -438,6 +454,7 @@ class ApplicationPartPropfind(ApplicationBase):
|
|||||||
http_depth = environ.get("HTTP_DEPTH", "0")
|
http_depth = environ.get("HTTP_DEPTH", "0")
|
||||||
permissions_filter = None
|
permissions_filter = None
|
||||||
shares: dict = {}
|
shares: dict = {}
|
||||||
|
allowed_items: list = []
|
||||||
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)
|
||||||
@@ -446,7 +463,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
|
shares[share['PathOrToken']] = 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
|
||||||
@@ -473,15 +490,16 @@ class ApplicationPartPropfind(ApplicationBase):
|
|||||||
return httputils.NOT_ALLOWED
|
return httputils.NOT_ALLOWED
|
||||||
# put item back
|
# put item back
|
||||||
items_iter = itertools.chain([item], items_iter)
|
items_iter = itertools.chain([item], items_iter)
|
||||||
allowed_items = list(self._collect_allowed_items(items_iter, user))
|
for item, permission in list(self._collect_allowed_items(items_iter, user)):
|
||||||
|
allowed_items.append((item, permission, None))
|
||||||
if self._sharing._enabled:
|
if self._sharing._enabled:
|
||||||
if http_depth == "1":
|
if http_depth == "1":
|
||||||
if logger.isEnabledFor(logging.DEBUG):
|
if logger.isEnabledFor(logging.DEBUG):
|
||||||
logger.debug("TRACE/PROPFIND: get shared collections")
|
logger.debug("TRACE/PROPFIND: get shared collections")
|
||||||
# check for shared collections related to user, Enabled and not Hidden
|
# check for shared collections related to user, Enabled and not Hidden
|
||||||
collections_share_map = self._sharing.sharing_collection_map_list(User=user, Enabled=True, Hidden=False)
|
collections_share_list = self._sharing.sharing_collection_list(User=user, Enabled=True, Hidden=False)
|
||||||
if collections_share_map:
|
if collections_share_list:
|
||||||
for share in collections_share_map:
|
for share in collections_share_list:
|
||||||
c_share = share['PathOrToken']
|
c_share = share['PathOrToken']
|
||||||
c_path = share['PathMapped']
|
c_path = share['PathMapped']
|
||||||
c_user = share['Owner']
|
c_user = share['Owner']
|
||||||
@@ -498,8 +516,10 @@ class ApplicationPartPropfind(ApplicationBase):
|
|||||||
with self._storage.acquire_lock("r", c_user):
|
with self._storage.acquire_lock("r", c_user):
|
||||||
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
|
for item, permission in c_allowed_items:
|
||||||
shares[c_path] = share
|
allowed_items.append((item, permission, share['ShareType']))
|
||||||
|
shares[c_share] = 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,
|
||||||
|
|||||||
Reference in New Issue
Block a user