Sharing Collections - Kozea/Radicale GitHub Wiki

Radicale v3

"radicale" is a lightwide CalDAV/CardDAV server and supporting sharing of collections only on server side with static configuration.

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:

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

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

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** ⚠️