Kozea / Radicale

A simple CalDAV (calendar) and CardDAV (contact) server.
https://radicale.org
GNU General Public License v3.0
3.27k stars 427 forks source link

404 not found when trying to subscribe to a calendar #1507

Open asio opened 3 months ago

asio commented 3 months ago

Radicale version: 3.2.0 OS: Debian 12 (bookworm)

I've have the following users:

  1. shared_user
  2. admin1
  3. admin2
  4. user1

I'm trying to setup two calendars owned by user shared_user

  1. events: This calendar should be read-only for all users including non-authencated users, and read/write for admin1 and admin2.
  2. meetings: This calendar should be read-only for authenticated(admin1, admin2, user1) users, and read/write for admin1 and admin2.

The error I get when trying to add a new Subscribe from web calendar in outlook.office.com is:

Jun 03 12:43:24 application radicale[2099082]: [2099082/Thread-1 (process_request_thread)] [INFO] GET response status for '/shared_user/events.ics' in 0.010 seconds: 404 Not Found

Directory structure

# Create directories for each user
sudo  mkdir /var/lib/radicale/collections
sudo chown radicale:radicale /var/lib/radicale/collections/
sudo -u radicale mkdir -p /var/lib/radicale/collections/{shared_user,admin1,admin2,user1}

# Create files for shared_user's calendars
sudo -u radicale touch /var/lib/radicale/collections/shared_user/{meetings,events}.ics

[drwxr-xr-x root     root    ]  /etc/radicale/
├── [-rw-r--r-- root     root    ]  config
├── [-rw-r--r-- root     root    ]  rights
└── [-rw-r--r-- radicale radicale]  users

[drwxr-xr-x root     root    ]  /var/lib/radicale/
└── [drwxr-xr-x radicale radicale]  collections
    ├── [drwxr-xr-x radicale radicale]  admin1
    ├── [drwxr-xr-x radicale radicale]  admin2
    ├── [drwxr-x--- radicale radicale]  collection-root
    ├── [drwxr-xr-x radicale radicale]  shared_user
    │   ├── [-rw-r--r-- radicale radicale]  events.ics
    │   └── [-rw-r--r-- radicale radicale]  meetings.ics
    └── [drwxr-xr-x radicale radicale]  user1

/etc/radicale/config


[server]
hosts = 127.0.0.1:5232

[encoding]

[auth]
type = htpasswd
htpasswd_filename = /etc/radicale/users
htpasswd_encryption = bcrypt

[rights]
type = from_file
file = /etc/radicale/rights

[storage]
type = multifilesystem
filesystem_folder = /var/lib/radicale/collections

[web]
type = internal

[logging]
level = debug

/etc/radicale/rights

# Allow reading root collection for authenticated users
[root]
user: .+
collection:
permissions: Rr

# Allow reading and writing principal collection
# (same as user name)
[owner_write]
user: .*
collection: {user}/.*
permissions: rw

# Allow reading and writing calendars and address books
# that are direct children of the principal collection
[owner_write_children]
user: .+
collection: {user}/[^/]+
permissions: RWrw

# Admins reading and writing shared calendars.
[shared_write]
user: admin1|admin2
collection: shared_user/(meetings|events)
permissions: rw

# Authenticated users reading shared calendars.
[shared_read]
user: user1
collection: shared_user/(meetings|events)
permissions: r

# Non-authenticated reading shared calendars.
[public_read]
user: .*
collection: shared_user/events.ics
permissions: r

nginx conf

server {
    server_name calendar.WITHHELD.se;

    listen 80;

    return 301 https://$host$request_uri;
}

server {
    server_name calendar.WITHHELD.se;

    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/calendar.WITHHELD.se/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/calendar.WITHHELD.se/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    add_header Strict-Transport-Security "max-age=31536000" always; # managed by Certbot

    ssl_trusted_certificate /etc/letsencrypt/live/calendar.WITHHELD.se/chain.pem; # managed by Certbot
    ssl_stapling on; # managed by Certbot
    ssl_stapling_verify on; # managed by Certbot

    location / {
        proxy_pass http://127.0.0.1:5232;
        proxy_set_header Host $host;
        proxy_set_header X-Real-Ip $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

radicale debug output

Jun 03 12:43:24 application radicale[2099082]: [2099082/Thread-1 (process_request_thread)] [INFO] GET request for '/shared_user/events.ics' received from 127.0.0.1 (forwarded for 'WITHHELD') Jun 03 12:43:24 application radicale[2099082]: [2099082/Thread-1 (process_request_thread)] [DEBUG] Request headers: {'CONTENT_LENGTH': '', 'CONTENT_TYPE': 'text/plain', 'GATEWAY_INTERFACE': 'CGI/1.1', 'HTTP_CONNECTION': 'close', 'HTTP_HOST': 'calendar.WITHHELD.se', 'HTTP_RANGE': 'bytes=0-511', 'HTTP_X_FORWARDED_FOR': 'WITHHELD', 'HTTP_X_FORWARDED_PROTO': 'https', 'HTTP_X_REAL_IP': 'WITHHELD', 'PATH_INFO': '/shared_user/events.ics', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_HOST': '', 'REQUEST_METHOD': 'GET', 'SCRIPT_NAME': '', 'SERVER_NAME': 'localhost', 'SERVER_PORT': '5232', 'SERVER_PROTOCOL': 'HTTP/1.0', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'wsgi.errors': <_io.TextIOWrapper name='' mode='w' encoding='utf-8'>, 'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>, 'wsgi.input': <_io.BufferedReader name=7>, 'wsgi.multiprocess': False, 'wsgi.multithread': True, 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.version': (1, 0)} Jun 03 12:43:24 application radicale[2099082]: [2099082/Thread-1 (process_request_thread)] [DEBUG] Base prefix (from SCRIPT_NAME): '' Jun 03 12:43:24 application radicale[2099082]: [2099082/Thread-1 (process_request_thread)] [DEBUG] Sanitized path: '/shared_user/events.ics' Jun 03 12:43:24 application radicale[2099082]: [2099082/Thread-1 (process_request_thread)] [DEBUG] Rule '':'shared_user/events.ics' doesn't match '.+':'' from section 'root' Jun 03 12:43:24 application radicale[2099082]: [2099082/Thread-1 (process_request_thread)] [DEBUG] Rule '':'shared_user/events.ics' doesn't match '.':'{user}/.' from section 'owner_write' Jun 03 12:43:24 application radicale[2099082]: [2099082/Thread-1 (process_request_thread)] [DEBUG] Rule '':'shared_user/events.ics' doesn't match '.+':'{user}/[^/]+' from section 'owner_write_children' Jun 03 12:43:24 application radicale[2099082]: [2099082/Thread-1 (process_request_thread)] [DEBUG] Rule '':'shared_user/events.ics' doesn't match 'admin1|admin2':'shared_user/(meetings|events)' from section 'shared_write' Jun 03 12:43:24 application radicale[2099082]: [2099082/Thread-1 (process_request_thread)] [DEBUG] Rule '':'shared_user/events.ics' doesn't match 'user1':'shared_user/(meetings|events)' from section 'shared_read' Jun 03 12:43:24 application radicale[2099082]: [2099082/Thread-1 (process_request_thread)] [DEBUG] Rule '':'shared_user/events.ics' matches '.*':'shared_user/events.ics' from section 'public_read' Jun 03 12:43:24 application radicale[2099082]: [2099082/Thread-1 (process_request_thread)] [DEBUG] Response content: The requested resource could not be found. Jun 03 12:43:24 application radicale[2099082]: [2099082/Thread-1 (process_request_thread)] [INFO] GET response status for '/shared_user/events.ics' in 0.010 seconds: 404 Not Found

pbiering commented 3 months ago

Hmm, I've implemented this by softlinking shared calendars to the user directory and restricting permissions to the names of the softlinks per user. And starting point of an exposed collection is always below "collection-root", so you have to move directories also.

asio commented 3 months ago

I'm trying to add the shared calender as a unauthenticated user to outlook, so I'm using the url:

https://calendar.domain.se/shared_user/events.ics

Also when recreating the directories and putting them under collection-root as follows:

[drwxr-xr-x root     root    ]  /etc/radicale/
├── [-rw-r--r-- root     root    ]  config
├── [-rw-r--r-- root     root    ]  rights
└── [-rw-r--r-- radicale radicale]  users
[drwxr-xr-x root     root    ]  /var/lib/radicale/
└── [drwxr-xr-x radicale radicale]  collections
    └── [drwxr-xr-x radicale radicale]  collection-root
        ├── [drwxr-xr-x radicale radicale]  admin1
        ├── [drwxr-xr-x radicale radicale]  admin2
        ├── [drwxr-xr-x radicale radicale]  shared_user
        │   ├── [-rw-r--r-- radicale radicale]  events.ics
        │   └── [-rw-r--r-- radicale radicale]  meetings.ics
        └── [drwxr-xr-x radicale radicale]  user1

I get the following error when I try and subscribe to the events calendar:

Jun 07 12:11:49 application radicale[2182722]: [2182722/Thread-1 (process_request_thread)] [ERROR] An exception occurred during GET request on '/shared_user/events.ics': Failed to load item 'events.ics' in 'shared_user': Item contains 0 components
                                               Traceback (most recent call last):
                                                 File "/usr/lib/python3/dist-packages/radicale/storage/multifilesystem/get.py", line 96, in _get
                                                   radicale_item.check_and_sanitize_items(
                                                 File "/usr/lib/python3/dist-packages/radicale/item/__init__.py", line 97, in check_and_sanitize_items
                                                   raise ValueError("Item contains %d components" % len(vobject_items))
                                               ValueError: Item contains 0 components

                                               The above exception was the direct cause of the following exception:

                                               Traceback (most recent call last):
                                                 File "/usr/lib/python3/dist-packages/radicale/app/__init__.py", line 108, in __call__
                                                   status_text, headers, answers = self._handle_request(environ)
                                                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                                 File "/usr/lib/python3/dist-packages/radicale/app/__init__.py", line 290, in _handle_request
                                                   status, headers, answer = function(
                                                                             ^^^^^^^^^
                                                 File "/usr/lib/python3/dist-packages/radicale/app/get.py", line 80, in do_GET
                                                   item = next(iter(self._storage.discover(path)), None)
                                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                                 File "/usr/lib/python3/dist-packages/radicale/storage/multifilesystem/discover.py", line 75, in discover
                                                   item = collection._get(href)
                                                          ^^^^^^^^^^^^^^^^^^^^^
                                                 File "/usr/lib/python3/dist-packages/radicale/storage/multifilesystem/get.py", line 104, in _get
                                                   raise RuntimeError("Failed to load item %r in %r: %s" %
                                               RuntimeError: Failed to load item 'events.ics' in 'shared_user': Item contains 0 components
pbiering commented 3 months ago

Can it be that there is a misunderstanding how collections are working related to directory setup?

Where are the related/required .Radicale.props files?

I have documented a example storage layout, potentially this helps

https://github.com/Kozea/Radicale/wiki/Collection-Storage