From 0f355114ae1858e1eea3b1fea157d0667bf2b0ac Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sat, 9 Mar 2024 06:43:39 +0100 Subject: [PATCH 1/3] add option for global permit of delete of collection (default: True to avoid breaking change) --- config | 3 +++ radicale/app/__init__.py | 3 +++ radicale/app/delete.py | 5 ++++- radicale/config.py | 4 ++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/config b/config index 4c283bbf4..5767dc9c3 100644 --- a/config +++ b/config @@ -79,6 +79,9 @@ # File for rights management from_file #file = /etc/radicale/rights +# Permit delete of a collection (global) +#permit_delete_collection = True + [storage] diff --git a/radicale/app/__init__.py b/radicale/app/__init__.py index 6896bc701..37ab6a5e7 100644 --- a/radicale/app/__init__.py +++ b/radicale/app/__init__.py @@ -68,6 +68,7 @@ class Application(ApplicationPartDelete, ApplicationPartHead, _max_content_length: int _auth_realm: str _extra_headers: Mapping[str, str] + _permit_delete_collection: bool def __init__(self, configuration: config.Configuration) -> None: """Initialize Application. @@ -84,6 +85,8 @@ def __init__(self, configuration: config.Configuration) -> None: self._max_content_length = configuration.get( "server", "max_content_length") self._auth_realm = configuration.get("auth", "realm") + self._permit_delete_collection = configuration.get("rights", "permit_delete_collection") + logger.info("permit delete of collection: %s", self._permit_delete_collection) self._extra_headers = dict() for key in self.configuration.options("headers"): self._extra_headers[key] = configuration.get("headers", key) diff --git a/radicale/app/delete.py b/radicale/app/delete.py index 69ae5ab40..313dedf73 100644 --- a/radicale/app/delete.py +++ b/radicale/app/delete.py @@ -68,7 +68,10 @@ def do_DELETE(self, environ: types.WSGIEnviron, base_prefix: str, # ETag precondition not verified, do not delete item return httputils.PRECONDITION_FAILED if isinstance(item, storage.BaseCollection): - xml_answer = xml_delete(base_prefix, path, item) + if self._permit_delete_collection: + xml_answer = xml_delete(base_prefix, path, item) + else: + return httputils.NOT_ALLOWED else: assert item.collection is not None assert item.href is not None diff --git a/radicale/config.py b/radicale/config.py index 02a0b3814..fc1612184 100644 --- a/radicale/config.py +++ b/radicale/config.py @@ -184,6 +184,10 @@ def _convert_to_bool(value: Any) -> bool: "help": "rights backend", "type": str_or_callable, "internal": rights.INTERNAL_TYPES}), + ("permit_delete_collection", { + "value": "True", + "help": "permit delete of a collection", + "type": bool}), ("file", { "value": "/etc/radicale/rights", "help": "file for rights management from_file", From 1c64fdc5b1893efd44bbe417464fc2d6be1d7552 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sat, 9 Mar 2024 06:52:34 +0100 Subject: [PATCH 2/3] update doc --- DOCUMENTATION.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index ebabc55bf..4d063d5ab 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -812,6 +812,12 @@ Default: `owner_only` File for the rights backend `from_file`. See the [Rights](#authentication-and-rights) section. +##### permit_delete_collection + +(New since 3.1.9) + +Global control of permission to delete complete collection (default: True) + #### storage ##### type From 21ebbca2d9b7c6222972b63bc835b59f8fd31897 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sat, 9 Mar 2024 07:04:02 +0100 Subject: [PATCH 3/3] add forgotten attribute definition --- radicale/app/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/radicale/app/base.py b/radicale/app/base.py index 4316117d1..9330ad1d6 100644 --- a/radicale/app/base.py +++ b/radicale/app/base.py @@ -38,6 +38,7 @@ class ApplicationBase: _rights: rights.BaseRights _web: web.BaseWeb _encoding: str + _permit_delete_collection: bool def __init__(self, configuration: config.Configuration) -> None: self.configuration = configuration