Merge pull request #2033 from pbiering/sharing-bday-add-desc

Sharing bday add description, doc updates, bugfixes
This commit is contained in:
Peter Bieringer
2026-03-21 09:39:30 +01:00
committed by GitHub
7 changed files with 131 additions and 108 deletions

View File

@@ -1,121 +1,29 @@
# Collection Sharing
Static collection sharing without permissions filter using soft-links (Unix-only) is supported since storage type `multifilesystem` was implemented, see (Wiki: Sharing Collections)[https://github.com/Kozea/Radicale/wiki/Sharing-Collections]
Static collection sharing without permissions filter using soft-links (Unix-only) is supported since storage type `multifilesystem` was implemented, see [Wiki: Sharing Collections](https://github.com/Kozea/Radicale/wiki/Sharing-Collections)
With _3.7.0_ major extension was implemented using internal mapping configuration stored in a database and a management API.
With _3.7.0_ major extension was implemented
* added internal mapping with configuration stored in a database
* added management API
* WebUI extension using the management API
## Sharing Implementation
Implemenation of sharing collections is done in case entry exists in sharing database by replacing provided data on request and adjust if required data in responses.
Implemenation of sharing collections is done by using a database to lookup the URI and in case entry exists by mapping to target URI and replacing provided data on request and adjust if required data in response.
Permissions are filtered by provided `Permissions`.
### CxDAV requests
## Sharing Configuration
#### CxDav request "(DELETE|GET|HEAD|PUT)"
* Actions
* map
* Lookup by
* `path` (provided in request)
* `user` (authenticated)
* Replace
* `user` by `Owner`
* `path` by `PathMapped`
* Activate
* `permissions_filter` by `Permissions`
#### CxDav request "REPORT"
* Actions
* map
* back-map response
* Lookup by
* `path` (provided in request)
* `user` (authenticated)
* Replace
* `user` by `Owner`
* `path` by `PathMapped`
* Activate
* `permissions_filter` by `Permissions`
#### CxDav request "PROPFIND" without HTTP_DEPTH=1
* Actions
* map
* back-map response
* overwrite `Properties` if provided
* Lookup by
* `path` (provided in request)
* `user` (authenticated)
* Replace
* `user` by `Owner`
* `path` by `PathMapped`
* Overlay
* `Properties` if provided
* Activate
* `permissions_filter` by `Permissions`
#### CxDav request "PROPFIND" with HTTP_DEPTH=1
* Actions
* extend list
* Lookup for active shares for `user` in sharing database
* Extend list if conditions are met
* `permissions_filter` by `Permissions`
#### CxDav request "PROPPATCH"
* Actions
* map
* adjust properties of a collection
* Lookup by
* `path` (provided in request)
* `user` (authenticated)
* Replace
* `user` by `Owner`
* `path` by `PathMapped`
* Activate
* `permissions_filter` by `Permissions`
* Depending on `permissions_filter`, global options and `Permissions`
* adjust properties of collection
* adjust whitelisted properties in `Properties` for overlay (see OVERLAY_PROPERTIES_WHITELIST)
#### CxDav request "(MKCALENDAR|MKCOL)"
* Action
* check for conflicts
* Lookup by
* `user` (authenticated)
* Verify for non-existence as `PathOrToken` in sharing database
* `path` (provided in request)
#### CxDav request "(MOVE)"
* Action
* map source
* map destination
* Lookup by
* `path` (provided in request)
* `user` (authenticated)
* `to_path` (provided in request)
* `to_user` (same as `user`)
* Replace
* `user` by `Owner` (of `path`)
* `path` by `PathMapped` (of path)
* `to_user` by `Owner` (of `to_path`)
* `to_path` by `PathMapped` (of `to_path`)
* Activate
* `permissions_filter` by `Permissions` (of `to_path`)
* `to_permissions_filter` by `Permissions` (of `to_path`)
New section `[sharing]` controls sharing configuration, see [DOCUMENTATION:Sharing](DOCUMENTATION.md#sharing) for details
## Sharing Configuration Store
Types of supported sharing configuration:
* csv (_>= 3.7.0_)
* files (_>= 3.7.0_)
* *csv* (_>= 3.7.0_)
* *files* (_>= 3.7.0_)
### Sharing Configuration Entry Data
@@ -152,6 +60,107 @@ If given, properties are stored in JSON format in CSV.
File-based configuration store is using encoded `PathOrToken` as filename for each config. File contains the data stored as "dict" in binary Python "pickle" format (same is also used for item cache files).
## Sharing Request Handling
### CxDAV requests
#### CxDav request "(DELETE|GET|HEAD|PUT)"
* Actions
* map
* Lookup by
* `path` (provided in request)
* `user` (authenticated)
* Replace
* `path` by `PathMapped`
* `user` by `Owner`
* Activate
* `permissions_filter` by `Permissions`
#### CxDav request "REPORT"
* Actions
* map
* back-map response
* Lookup by
* `path` (provided in request)
* `user` (authenticated)
* Replace
* `path` by `PathMapped`
* `user` by `Owner`
* Activate
* `permissions_filter` by `Permissions`
#### CxDav request "PROPFIND" without HTTP_DEPTH=1
* Actions
* map
* back-map response
* overwrite `Properties` if provided
* Lookup by
* `path` (provided in request)
* `user` (authenticated)
* Replace
* `path` by `PathMapped`
* `user` by `Owner`
* Overlay
* `Properties` if provided
* Activate
* `permissions_filter` by `Permissions`
#### CxDav request "PROPFIND" with HTTP_DEPTH=1
* Actions
* extend list
* Lookup for active shares for `user` in sharing database
* Extend list if conditions are met
* `permissions_filter` by `Permissions`
#### CxDav request "PROPPATCH"
* Actions
* map
* adjust properties of a collection
* Lookup by
* `path` (provided in request)
* `user` (authenticated)
* Replace
* `path` by `PathMapped`
* `user` by `Owner`
* Activate
* `permissions_filter` by `Permissions`
* Depending on `permissions_filter`, global options and `Permissions`
* adjust properties of collection
* adjust whitelisted properties in `Properties` for overlay (see OVERLAY_PROPERTIES_WHITELIST)
#### CxDav request "(MKCALENDAR|MKCOL)"
* Action
* check for conflicts
* Lookup by
* `user` (authenticated)
* Verify for non-existence as `PathOrToken` in sharing database
* `path` (provided in request)
#### CxDav request "(MOVE)"
* Action
* map source
* map destination
* Lookup by
* `path` (provided in request)
* `user` (authenticated)
* `to_path` (provided in request)
* `to_user` (same as `user`)
* Replace
* `path` by `PathMapped` (of path)
* `user` by `Owner` (of `path`)
* `to_path` by `PathMapped` (of `to_path`)
* `to_user` by `Owner` (of `to_path`)
* Activate
* `permissions_filter` by `Permissions` (of `to_path`)
* `to_permissions_filter` by `Permissions` (of `to_path`)
## Sharing Access
### Sharing Access via Maps
@@ -579,7 +588,7 @@ Whitelisted ones are defined in `OVERLAY_PROPERTIES_WHITELIST` in `radicale/shar
#### Requirements
* sharing / permit_properties_overlay = True
* `permit_properties_overlay = True`
#### Test sequence

View File

@@ -549,8 +549,12 @@ class Application(ApplicationPartDelete, ApplicationPartHead,
else:
profiler_active = True
status, headers, answer, xml_request = function(
environ, base_prefix, path, user, remote_host, remote_useragent)
try:
status, headers, answer, xml_request = function(
environ, base_prefix, path, user, remote_host, remote_useragent)
except PermissionError as e:
logger.error("PermissionError: %s", e)
status, headers, answer, xml_request = httputils.INTERNAL_SERVER_ERROR
# Profiling
if self._profiling_per_request:

View File

@@ -537,6 +537,7 @@ class Item:
pass
bdayS = match[1] + match[2] + match[3]
bdaySdesc = match[1] + "-" + match[2] + "-" + match[3]
bdayY = int(match[1])
bdayM = int(match[2])
bdayD = int(match[3])
@@ -594,6 +595,9 @@ class Item:
# add transparency
item_ics.vevent.add('transp').value = "TRANSPARENT"
# add description
item_ics.vevent.add('description').value = "BDAY=" + bdaySdesc
href = self.href
if href is not None:
href = href.rstrip(".vcf") + ".ics"

View File

@@ -266,7 +266,9 @@ class Sharing(sharing.BaseSharing):
"HiddenByOwner": HiddenByOwner,
"HiddenByUser": HiddenByUser,
"TimestampCreated": Timestamp,
"TimestampUpdated": Timestamp}
"TimestampUpdated": Timestamp,
"Properties": Properties}
if logger.isEnabledFor(logging.DEBUG):
logger.debug("TRACE/sharing/*/create: add row: %r", row)
self._sharing_cache.append(row)

View File

@@ -244,7 +244,8 @@ class Sharing(sharing.BaseSharing):
"HiddenByOwner": HiddenByOwner,
"HiddenByUser": HiddenByUser,
"TimestampCreated": Timestamp,
"TimestampUpdated": Timestamp}
"TimestampUpdated": Timestamp,
"Properties": Properties}
version = DB_VERSION

View File

@@ -942,6 +942,7 @@ class TestSharingApiSanity(BaseTest):
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
json_dict['Properties'] = {"D:displayname": "Test"}
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
@@ -962,6 +963,7 @@ class TestSharingApiSanity(BaseTest):
assert answer_dict['Content'][0]['HiddenByOwner'] is True
assert answer_dict['Content'][0]['HiddenByUser'] is True
assert answer_dict['Content'][0]['Permissions'] == "r"
assert answer_dict['Content'][0]['Properties'] == {"D:displayname": "Test"}
logging.info("\n*** enable map by owner (json->json) -> 200")
json_dict = {}
@@ -4288,6 +4290,7 @@ permissions: RrWw""")
assert "DTSTART;VALUE=DATE:19700101" in answer
assert "DTEND;VALUE=DATE:19700102" in answer
assert "TRANSP:TRANSPARENT" in answer
assert "DESCRIPTION:BDAY=1970-01-01" in answer
# verify report as user
logging.info("\n*** REPORT collection user -> ok")

View File

@@ -60,7 +60,7 @@ setup(
license="GNU GPL v3",
platforms="Any",
packages=find_packages(
exclude=["*.tests", "*.tests.*", "tests.*", "tests"]),
exclude=["*.tests", "*.tests.*", "tests.*", "tests", "integ_tests"]),
package_data={"radicale": [*web_files, "py.typed"]},
entry_points={"console_scripts": ["radicale = radicale.__main__:run"]},
install_requires=install_requires,