Static collection sharing without permissions filter using soft-links (Unix-only) is supported since storage type multifilesystem was implemented, see Wiki: Sharing Collections
With 3.7.0 major extension was implemented
Implementation 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.
New section [sharing] controls sharing configuration, see DOCUMENTATION:Sharing for details
Types of supported sharing configuration:
ShareType: type of share
token: token-based share (do not require user authentication)map: map-based share (requires user authentication)PathOrToken: token or "virtual" collection, has to be unique (PRIMARY KEY)PathMapped: target collectionConversion: conversion methodOwner: owner of the shareUser: user of the sharePermissions: effective permission of the shareEnabledByOwner: control by ownerEnabledByUser: control by userHiddenByOwner: control by ownerHiddenByUser: control by userTimestampCreated: unixtime of creationTimestampUpdated: unixtime of last updateProperties: overlay properties (limited set whitelisted)Actions: (reserved for future usage)Enabled*: owner AND user have to enable a share to become usable
Hidden*: owner AND user have to disable a share to become visible in PROPFIND
none: no conversionbday: auto-mapping on-the-fly a VADDRESSBOOK to a VCALENDAR of all entries containing a BDAY
One CSV file containing one row per sharing config, separated by ; and containing header with columns from above.
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).
path (provided in request)user (authenticated)path by PathMappeduser by Ownerpermissions_filter by Permissionspath (provided in request)user (authenticated)path by PathMappeduser by Ownerpermissions_filter by PermissionsProperties if providedpath (provided in request)user (authenticated)path by PathMappeduser by OwnerProperties if providedpermissions_filter by Permissionsuser in sharing databasepermissions_filter by Permissionspath (provided in request)user (authenticated)path by PathMappeduser by Ownerpermissions_filter by Permissionspermissions_filter, global options and Permissions
Properties for overlay (see OVERLAY_PROPERTIES_WHITELIST)user (authenticated)PathOrToken in sharing database
path (provided in request)path (provided in request)user (authenticated)to_path (provided in request)to_user (same as user)path by PathMapped (of path)user by Owner (of path)to_path by PathMapped (of to_path)to_user by Owner (of to_path)permissions_filter by Permissions (of to_path)to_permissions_filter by Permissions (of to_path)Map-based sharing can be accessed as usual after authentication and authorization.
permit_create_map
MmIn case share should be visible using PROPFIND
Token-based sharing can be accessed after retrieving the token via
Token-URI: /.token/<Token>
Note: requests to not enabled or not even defined tokens will result in 401 Not Authorized
permit_create_token
TtType: POST API
Base-URI: /.sharing/v1/<ShareType>/<Hook>
See also test cases in radicale/tests/test_sharing.py
Parsing be controlled by CONTENT_TYPE
Can be selected by HTTP_ACCEPT - default is equal to provided CONTENT_TYPE
PathOrToken: token or "virtual" collectionPathMapped: target collectionOwner: owner of the shareUser: user of the sharePermissions: effective permission of the shareEnabled: owner/user selected by authenticationHidden: owner/user selected by authenticationProperties: properties to overlayShows what is active/supported like ShareTypes(Feature), Conversions or permission to create or use properties overlay (depending on config options)
Output: text/plain|application/json
Examples
curl -u user:$userpw -H "accept: text/plain" -d "" http://localhost:5232/.sharing/v1/all/info
ApiVersion=1
Status='success'
FeatureEnabledCollectionByMap=True
PermittedCreateCollectionByMap=True
FeatureEnabledCollectionByToken=True
PermittedCreateCollectionByToken=True
SupportedConversions=(bday none)
PermittedPropertiesOverlay=True
SupportedPropertiesOverlay=(C:calendar-description ICAL:calendar-color CR:addressbook-description INF:addressbook-color D:displayname)
bash
curl -u user:$userpw --silent -H "accept: application/json" -d "" http://localhost:5232/.sharing/v1/all/info | jq
{
"ApiVersion": 1,
"Status": "success",
"FeatureEnabledCollectionByMap": true,
"PermittedCreateCollectionByMap": true,
"FeatureEnabledCollectionByToken": true,
"PermittedCreateCollectionByToken": true,
"SupportedConversions": ["bday", "none"],
"PermittedPropertiesOverlay": true,
"SupportedPropertiesOverlay": ["C:calendar-description", "ICAL:calendar-color", "CR:addressbook-description", "INF:addressbook-color", "D:displayname"]
}
OwnerCreate a share by mapping a collection of an Owner to a token.
Authorization
PathMapped is existing and a collectionOwner has at least read access to PathMappedpermit_create_token = True or rights permission tpermit_create_token = False or rights permission TInput
| Parameter | Type | Requirement | | - | - | - | | PathMapped | str | mandatory | | Conversion | str | optional (default:none) | | User | str | optional (default:owner) | | Permissions | str | optional (default:rp) | | Enabled | bool | optional (owner/default:False) | | Hidden | bool | optional (owner/default:True) | | Properties | str | optional |
| Parameter | Type | Value | | - | - | - | | PathOrToken | str | (autogenerated token) |
curl -u user:$userpw -d "PathMapped=/user/testcalendar1/" -d "Enabled=True" -d "Hidden=False" http://localhost:5232/.sharing/v1/token/create
ApiVersion=1
Status='success'
PathOrToken='/.token/v1/VQR7AmsVRi2ZlFj_JwGpFx-ES5Goyku-gP_YkLh1zUw0/'
curl -u user:$userpw -H "Content-Type: application/json" -d '{ "PathMapped": "/user/testcalendar1/", "Enabled": true, "Hidden": false}' http://localhost:5232/.sharing/v1/token/create
{"ApiVersion": 1, "Status": "success", "PathOrToken": "/.token/v1/aMsmGqOsRwSH-2-6tEa8EMr4RMYzMU7WvPmjnp5qDnw0/"}
Create a share by mapping a collection of an Owner to an User.
Authorization
PathMapped is existing and a collectionPathMapped is not existing already as a share target for same UserOwner has at least read access to PathMappedUser has at least read access to PathOrTokenpermit_create_map = True or rights permission mpermit_create_map = False or rights permission MInput
| Parameter | Type | Requirement | | - | - | - | | PathOrToken | str | mandatory | | PathMapped | str | mandatory | | Conversion | str | optional (default:none) | | User | str | mandatory | | Permissions | str | optional (default:r) | | Enabled | bool | optional (owner/default:False) | | Hidden | bool | optional (owner/default:True) | | Properties | optional |
Output: text/plain|application/json
Examples:
curl -u owner:$ownerpw -d "PathOrToken=/user/cal1-from-owner/" -d "PathMapped=/owner/testcalendar1/" -d "User=user" -d "Enabled=True" -d "Hidden=False" http://localhost:5232/.sharing/v1/map/create
ApiVersion=1
Status='success'
curl -u owner:$ownerpw -H "Content-Type: application/json" -d '{ "PathOrToken": "/user/cal1-from-owner/", "PathMapped": "/owner/testcalendar1/", "User" : "user", "Enabled": true, "Hidden": false}' http://localhost:5232/.sharing/v1/map/create
{"ApiVersion": 1, "Status": "success"}
List shares (optional with filter) either owned or assigned as user.
Authorization
Owner or UserInput
| Parameter | Type | Used for | | - | - | - | | PathOrToken | str | optional | | PathMapped | str | optional |
Output: text/plain|text/csv|application/json
Examples
curl -u user:$userpw -d "" http://localhost:5232/.sharing/v1/all/list
ApiVersion=1
Lines=1
Status='success'
Fields="ShareType;PathOrToken;PathMapped;Owner;User;Permissions;EnabledByOwner;EnabledByUser;HiddenByOwner;HiddenByUser;TimestampCreated;TimestampUpdated;Properties"
Content[0]="map;/user/cal1-from-owner/;/owner/testcalendar1/;owner;user;r;True;True;False;False;1772748001;1772748163;
curl -H "accept: text/csv" -u user:$userpw -d "" http://localhost:5232/.sharing/v1/map/list
ShareType;PathOrToken;PathMapped;Owner;User;Permissions;EnabledByOwner;EnabledByUser;HiddenByOwner;HiddenByUser;TimestampCreated;TimestampUpdated;Properties
map;/user/cal1-from-owner/;/owner/testcalendar1/;owner;user;r;True;False;False;True;1772747277;1772747277;
jqcurl -s -H "Content-Type: application/json" -u user:$userpw -d "{}" http://localhost:5232/.sharing/v1/all/list | jq
{
"ApiVersion": 1,
"Lines": 2,
"Status": "success",
"Content": [
{
"ShareType": "map",
"PathOrToken": "/user/cal1-from-owner/",
"PathMapped": "/owner/testcalendar1/",
"Owner": "owner",
"User": "user",
"Permissions": "r",
"EnabledByOwner": true,
"EnabledByUser": false,
"HiddenByOwner": false,
"HiddenByUser": true,
"TimestampCreated": 1772747277,
"TimestampUpdated": 1772747277,
"Properties": ""
},
{
"ShareType": "token",
"PathOrToken": "v1/DUSl_J5rRlWx3fy8YRXpH22FFllplkOTpcSwfGtpvkc=",
"PathMapped": "/user/testcalendar1/",
"Owner": "user",
"User": "user",
"Permissions": "r",
"EnabledByOwner": true,
"EnabledByUser": false,
"HiddenByOwner": false,
"HiddenByUser": true,
"TimestampCreated": 1772747371,
"TimestampUpdated": 1772747371,
"Properties": ""
}
7]
}
Delete a share selected by PathOrToken.
Authorization
OwnerInput
| Parameter | Type | Used for | as Owner | as User | | - | - | - | - | - | | PathOrToken | str | selection | mandatory | not-permitted |
Output: text/plain|application/json
Examples:
curl -u owner:$ownerpw -d "PathOrToken=/user/cal1-from-owner/" http://localhost:5232/.sharing/v1/map/delete
ApiVersion=1
Status='success'
curl -u user:$userpw -H "Content-Type: application/json" -d '{ "PathOrToken": "v1/DUSl_J5rRlWx3fy8YRXpH22FFllplkOTpcSwfGtpvkc="}' http://localhost:5232/.sharing/v1/token/delete
{"ApiVersion": 1, "Status": "success"}
Update a share selected by PathOrToken.
Execute delete+create in case PathOrToken needs to be changed.
Authorization
Owner or UserInput
| Parameter | Type | Used for | Owner | User | | - | - | - | - | - | | PathOrToken | str | selection | mandatory | mandatory | | PathMapped | str | adjust | optional | not-permitted | | User | str | adjust | optional | not-permitted | | Permissions | str | adjust | optional | not-permitted | | Enabled | bool | adjust | optional(owner) | optional(user) | | Hidden | bool | adjust | optional(owner) | optional(user) | | Properties | str | adjust | optional | optional |
Output: text/plain|application/json
Examples:
curl -u user:$userpw -d "PathOrToken=/user/cal1-from-owner/" -d "Enabled=True" -d "Hidden=False" http://localhost:5232/.sharing/v1/map/update
ApiVersion=1
Status='success'
curl -u user:$userpw -H "Content-Type: application/json" -d '{ "PathOrToken": "/user/cal1-from-owner/", "Enabled": true, "Hidden": false}' http://localhost:5232/.sharing/v1/map/update
{"ApiVersion": 1, "Status": "success"}
Toggle enable|disable|hide|unhide of Owner or User of a share selected by PathOrToken
Authorization
Owner or UserPathOrToken is existing and either owned or assigned to userInput
| Parameter | Type | Used for | Owner | User | | - | - | - | - | - | | PathOrToken | selection | mandatory | mandatory |
curl -u user:$userpw -d "PathOrToken=/user/cal1-from-owner/" http://localhost:5232/.sharing/v1/map/enable
ApiVersion=1
Status='success'
curl -u user:$userpw -H "Content-Type: application/json" -d '{ "PathOrToken": "/user/cal1-from-owner/"}' http://localhost:5232/.sharing/v1/map/unhide
{"ApiVersion": 1, "Status": "success"}
Owner or user can define per share a set of properties to overlay on PROPFIND response during create or update via API.
Whitelisted ones are defined in OVERLAY_PROPERTIES_WHITELIST in radicale/sharing/__init__.py:
C:calendar-descriptionICAL:calendar-colorCR:addressbook-descriptionINF:addressbook-colorD:displaynamepermit_properties_overlay
Ppenforce_properties_overlay
Eepermission of particular share configuration: p or P
permission based on rights per location: p or P
config option: permit_properties_overlay
permission of particular share configuration: e or E
permission based on rights per location: e or E
config option: enforce_properties_overlay
permit_properties_overlay = True## PROPFIND color
xml_pfc='<?xml version="1.0"?>
<propfind xmlns="DAV:" xmlns:ICAL="http://apple.com/ns/ical/">
<prop>
<ICAL:calendar-color />
</prop>
</propfind>'
## PROPPATCH color
xml_ppc='<?xml version="1.0"?>
<D:propertyupdate xmlns:D="DAV:">
<D:set>
<D:prop>
<I:calendar-color xmlns:I="http://apple.com/ns/ical/">#DDDDDD</I:calendar-color>
</D:prop>
</D:set>
</D:propertyupdate>'
## Retrieve collection color of owner (no color set)
curl -u owner:$ownerpw -d "$xml_pfc" -X PROPFIND http://localhost:5232/owner/testcalendar1/
## Create read-only share for user
curl -u owner:$ownerpw -d "PathOrToken=/user/cal1-from-owner/" -d "PathMapped=/owner/testcalendar1/" -d "User=user" -d "Enabled=True" -d "Hidden=False" http://localhost:5232/.sharing/v1/map/create
## Accept (enable+unhide) share by user
curl -u user:$userpw -d "PathOrToken=/user/cal1-from-owner/" -d "Enabled=True" -d "Hidden=False" http://localhost:5232/.sharing/v1/map/update
## Retrieve collection color of share by user (no color set)
curl -u user:$userpw -d "$xml_pfc" -X PROPFIND http://localhost:5232/user/cal1-from-owner/
## Set property overlay by user
curl -u user:$userpw -d "PathOrToken=/user/cal1-from-owner/" -d 'Properties="ICAL:calendar-color"="#CCCCCC"' http://localhost:5232/.sharing/v1/map/update
## Retrieve collection color of share by user (color set)
curl -u user:$userpw -d "$xml_pfc" -X PROPFIND http://localhost:5232/user/cal1-from-owner/
## Delete property overlay by user
curl -u user:$userpw -d "PathOrToken=/user/cal1-from-owner/" -d 'Properties=' http://localhost:5232/.sharing/v1/map/update
## Retrieve collection color of share by user (no color set)
url -u user:$userpw -d "$xml_pfc" -X PROPFIND http://localhost:5232/user/cal1-from-owner/
## Add property overlay by user using PROPPATCH
curl -u user:$userpw -d "$xml_ppc" -X PROPPATCH http://localhost:5232/user/cal1-from-owner/
## Retrieve collection color of share by user (color set)
curl -u user:$userpw -d "$xml_pfc" -X PROPFIND http://localhost:5232/user/cal1-from-owner/
Owner can create for itself or for particular user a virtual bday collection from an existing addressbook.
Preconditions:
sharing:
collection_by_mappermit_create_map## Create sharing of type *map* with conversion *bday*
curl -u owner:$ownerpw -d "PathOrToken=/owner/bday-of-addressbook/" -d "PathMapped=/owner/addressbook/" -d "User=owner" -d "Conversion=bday" http://localhost:5232/.sharing/v1/map/create
## Enable
curl -u owner:$ownerpw -d "PathOrToken=/owner/bday-of-addressbook/" http://localhost:5232/.sharing/v1/map/enable
## Unhide
curl -u owner:$ownerpw -d "PathOrToken=/owner/bday-of-addressbook/" http://localhost:5232/.sharing/v1/map/unhide
## Fetch VCALENDAR auto-created from VADDRESSBOOK
curl -u owner:$ownerpw http://localhost:5232/owner/bday-of-addressbook/
BEGIN:VCALENDAR
...
END:VCALENDAR
Via WebUI an additional (virtual) calendar collection appears
## Create sharing of type *token* with conversion *bday* (shown PathOrToken is an example)
curl -u owner:$ownerpw -d "Enabled=true" -d "Hidden=false" -d "PathMapped=/owner/addressbook/" -d "User=owner" -d "Conversion=bday" http://localhost:5232/.sharing/v1/token/create
ApiVersion=1
Status='success'
PathOrToken='/.token/v1/lqqwqhZYTGi9uSPsixien_8G5jiSK0FfhNFRGG_t8UA0/'
## Fetch VCALENDAR auto-created from VADDRESSBOOK
curl http://localhost:5232/.token/v1/lqqwqhZYTGi9uSPsixien_8G5jiSK0FfhNFRGG_t8UA0/
BEGIN:VCALENDAR
...
END:VCALENDAR