Sharing Collections - Kozea/Radicale GitHub Wiki

Overview

Collection Sharing per user

Method Supported File System Requirement
Static via softlink >= 2.0.0 softlink-support
Flexible via API >= 3.7.0 (none)

Collection Sharing per group membership

Method Supported File System Requirement
Static config >= 3.3.0 (1) case-sensitive
Flexible via API >= 3.8.0 (none)

(1) started with LDAP, since 3.8.0 group membership handling is extended

Collection Sharing per user's realm

Method Supported File System Requirement
Flexible via API >= 3.8.0 (none)

Flexible collection sharing

(>= 3.7.0 per user)

(>= 3.8.0 per user's group membership or realm)

Flexible collection sharing is supported on any operating system by using internal database and internal overlay mechanisms including on-the-fly conversions: https://github.com/Kozea/Radicale/blob/master/SHARING.md

Static collection sharing per group

(>= 3.3.0) LDAP

(>= 3.8.0) LDAP / PAM / htgroup file

See LDAP-authentication section of Radicale's wiki

Static collection sharing per user

Static collection sharing is supported only on Unix systems taking advantage of softlinked directories.

Sharing Collections with other configured users

Preparations

  • create a directory aside collection-root (which is the base directory of "radicale" to lookup user folders) like e.g. collection-shared
  • create a sub-directory structure with collections or group->collections
  • softlink required collection directory into user's directory
  • in case of read-only permissions are required for shared calendar, extend rights file matching particular collection and user

Example for a storage layout incl. shared calendar/addressbook

Note: */.Radicale* files/directories are not shown here

  • [d]: directory
  • [f]: file
  • [l]: softlink
  • (ro): read-only
  • (rw): read-write
[d] /var
└─[d] /lib
  └─[d] /radicale
    └─[d] /collections
      ├─[d] /collection-shared
      │ ├─[d] /group1
      │ │ ├─[d] /sharedcalendar1 (Collection)
      │ │ │ ├─[f] sharedschedule1.ics
      │ │ │ ├─[f] ...
      │ │ │ └─[f] sharedscheduleX.ics
      │ │ └─[d] /sharedaddressbook1 (Collection)
      │ │   ├─[f] sharedcontact1.vcf
      │ │   ├─[f] ...
      │ │   └─[f] sharedcontactX.vcf
      │ │
      │ └─[d] /group2
      │   ├─[d] /sharedcalendar2 (Collection)
      │   │ ├─[f] sharedschedule1.ics
      │   │ ├─[f] ...
      │   │ └─[f] sharedscheduleX.ics
      │   └─[d] /sharedaddressbook2 (Collection)
      │     ├─[f] sharedcontact1.vcf
      │     ├─[f] ...
      │     └─[f] sharedcontactX.vcf
      │
      └─[d] /collection-root
        ├─[d] /USER1 (share some collections of "group1")
        │ ├─[d] mycalendar1 (Collection)
        │ ├─[d] myaddressbook1 (Collection)
        │ ├─[l] sharedcalendar1 -> ../../collection-shared/group1/sharedcalendar1 (rw, default)
        │ └─[l] sharedaddressbook1 -> ../../collection-shared/group1/sharedaddressbook1 (rw, default)
        │
        ├─[d] /USER2 (share some collections of "group2" read-only)
        │ ├─[d] mycalendar1 (Collection)
        │ ├─[d] myaddressbook1 (Collection)
        │ ├─[l] sharedcalendar2 -> ../../collection-shared/group2/sharedcalendar2 (ro)
        │ └─[l] sharedaddressbook2 -> ../../collection-shared/group2/sharedaddressbook2 (ro)

Related rights extension for read-only "r":

[group2-sharedcalendar2-ro]
user: USER2
collection: {user}/sharedcalendar2(/.+)?
permissions: r

[group2-sharedaddressbook2-ro]
user: USER2
collection: {user}/sharedaddressbook2(/.+)?
permissions: r

Related rights extension for read-write "rw" but forbid-to-delete-collection-itself "d":

[group1-sharedcalendar1-rw]
user: USER1
collection: {user}/sharedcalendar1(/.+)?
permissions: rwd
  
[group1-sharedaddressbook1-rw]
user: USER2
collection: {user}/sharedaddressbook1(/.+)?
permissions: rwd

Sharing a collections read-only to public as WebCAL

ATTENTION: This is a simple and potentially insecure example

Requirements

  • A reverse proxy in front of "radicale" is mandatory, because "radicale" itself is not supporting mix of authenticated and unauthenticated users.

Preparations

  • create/assign a directory structure (see below)
  • create users ADMIN1 and ANON1 with secret passwords
  • extend the rights file to limit the user ANON1 only to GET requests ("i")
[anon]
user: ANON1
collection: {user}(/.*)?$
permissions: i
  • extend the reverse proxy configuration by catching requests in a location, map and enrich the request with credentials of user ANON1 by conditionally adding the authorization header to the request.
        <Location /public>
                RewriteRule /public/(events[a-z0-9]+).ics$ /radicale/ANON1/public$1/ [PT]
        </Location>

        SetEnvIf Request_URI "^/public/(events[a-z0-9]+)\.ics$" ANON_PUBLICEVENTS
        # "Basic <base64 encodede USER:PASS>" example generated with 'echo -n "ANON1:ANON1" | base64'
        RequestHeader set Authorization "Basic QU5PTjE6QU5PTjE=" env=ANON_PUBLICEVENTS

Example for a storage layout

[d] /var
└─[d] /lib
  └─[d] /radicale
    └─[d] /collections
      ├─[d] /collection-shared
      │ └─[d] /public
      │   └─[d] /publicevents1 (Collection)
      │     ├─[f] sharedschedule1.ics
      │     ├─[f] ...
      │     └─[f] sharedscheduleX.ics
      │
      └─[d] /collection-root
        ├─[d] /ADMIN1
        │ └─[l] publicevents1 -> ../../collection-shared/public/publicevents1 (rw, default)
        │
        └─[d] /ANON1
          └─[l] publicevents1 -> ../../collection-shared/public/publicevents1 (ro, only GET allowed)

Testing

GET request to a valid collection

(here it has 2 entries currently)

curl -s http://localhost/public/events1.ics | grep -E '(VEVENT|VCALENDAR)'
BEGIN:VCALENDAR
BEGIN:VEVENT
END:VEVENT
BEGIN:VEVENT
END:VEVENT
END:VCALENDAR

GET request to a non-existent collection

curl -s  http://localhost/publicevents2.ics
The requested resource could not be found.

PROPFIND request to URL

(expected forbidden)

curl --request PROPFIND http://localhost/public/events1.ics
Access to the requested resource forbidden.

PUT request to URL

(expected forbidden)

curl --data-binary @test.ics --request PUT http://localhost/public/events1.ics
Access to the requested resource forbidden.

Publishing

URL for publishing e.g. via WebCAL: https://FQDN/public/events1.ics

⚠️ **GitHub.com Fallback** ⚠️