Merge pull request #2189 from pbiering/issue-2187

Sharing-by-map: add support for collection requests missing a trailing /
This commit is contained in:
Peter Bieringer
2026-08-01 10:44:32 +03:00
committed by GitHub
3 changed files with 41 additions and 4 deletions

View File

@@ -2,6 +2,7 @@
## 3.7.8.dev
* Fix: sharing/proppatch: reject in case of write-access but 'p' is in permissions
* Fix: sharing/by-map: catch collection path without trailing / (supporting "pimsync")
## 3.7.7
* Fix: web plugin helpers httputils.serve_resource/serve_folder ignored their mimetypes and fallback_mimetype parameters and always used the built-in mapping, so custom web plugins could not serve additional file types with a correct Content-Type

View File

@@ -628,6 +628,7 @@ class BaseSharing:
""" returning dict with PathMapped, Owner, Permissions or None if invalid"""
if self.sharing_collection_by_map:
logger.trace("sharing/map/resolver: check path: %r", path)
# check collection path
result = self.database_get_sharing(
ShareType="map",
PathOrToken=path,
@@ -635,7 +636,7 @@ class BaseSharing:
User=user)
if not result:
# fallback to parent path
# assume item path, fallback to parent path
parent_path = pathutils.parent_path(path)
logger.trace("sharing/map/resolver: check parent path: %r", parent_path)
result = self.database_get_sharing(
@@ -646,9 +647,19 @@ class BaseSharing:
if result:
result['PathMapped'] = path.replace(parent_path, result['PathMapped'])
logger.trace("sharing/map/resolver: PathMapped=%r Permissions=%r by parent_path=%r", result['PathMapped'], result['Permissions'], parent_path)
else:
logger.trace("sharing/map/resolver: not found")
return None
if not result and not path.endswith("/"):
# assume collection path with missing trailing /
path_with_trailing_slash = path + "/"
logger.trace("sharing/map/resolver: check path having '/' appended: %r", path_with_trailing_slash)
result = self.database_get_sharing(
ShareType="map",
PathOrToken=path_with_trailing_slash,
OnlyEnabled=False,
User=user)
if result:
path = path_with_trailing_slash
logger.trace("sharing/map/resolver: PathMapped=%r Permissions=%r by path_with_trailing_slash=%r", result['PathMapped'], result['Permissions'], path_with_trailing_slash)
if result:
if result['EnabledByOwner'] is not True:
@@ -666,6 +677,7 @@ class BaseSharing:
logger.info("sharing/%s: resolved path %r->%r, user %r->%r, Permissions=%r Conversion=%r", "map", result['PathOrToken'], result['PathMapped'], user, result['Owner'], result['Permissions'], result['Conversion'])
return result
logger.trace("sharing/map/resolver: not found")
return None
else:
logger.trace("sharing/map: not active")

View File

@@ -1988,10 +1988,34 @@ class TestSharingApiSanity(BaseTest):
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
# check GET as user
logging.info("\n*** GET collection user -> ok")
_, headers, answer = self.request("GET", path_shared, check=200, login="user:userpw")
# check REPORT as user
logging.info("\n*** REPORT collection user -> ok")
_, responses = self.report(path_shared, """\
<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop xmlns:D="DAV:">
<D:getetag />
</D:prop>
</C:calendar-query>""", login="user:userpw")
assert len(responses) == 1
logging.info("response: %r", responses)
response = responses[path_shared_item]
assert isinstance(response, dict)
status, prop = response["D:getetag"]
assert status == 200 and prop.text
# check GET as user without trailing /
logging.info("\n*** GET collection user (without trailing /) -> ok")
_, headers, answer = self.request("GET", path_shared.removesuffix("/"), check=200, login="user:userpw")
# check REPORT as user without trailing /
logging.info("\n*** REPORT collection user (without trailing /) -> ok")
_, responses = self.report(path_shared.removesuffix("/"), """\
<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop xmlns:D="DAV:">
<D:getetag />