Merge pull request #1990 from pbiering/feature-sharing-prepare
Feature sharing prepare
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# This file is part of Radicale - CalDAV and CardDAV server
|
||||
# Copyright © 2020 Unrud <unrud@outlook.com>
|
||||
# Copyright © 2024-2024 Peter Bieringer <pb@bieringer.de>
|
||||
# Copyright © 2024-2026 Peter Bieringer <pb@bieringer.de>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
|
||||
import io
|
||||
import logging
|
||||
import posixpath
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
from typing import Optional
|
||||
from typing import Optional, Union
|
||||
|
||||
from radicale import (auth, config, hook, httputils, pathutils, rights,
|
||||
storage, types, utils, web, xmlutils)
|
||||
from radicale.log import logger
|
||||
from radicale.rights import intersect
|
||||
|
||||
# HACK: https://github.com/tiran/defusedxml/issues/54
|
||||
import defusedxml.ElementTree as DefusedET # isort:skip
|
||||
@@ -106,15 +106,19 @@ class Access:
|
||||
permissions: str
|
||||
_rights: rights.BaseRights
|
||||
_parent_permissions: Optional[str]
|
||||
_permissions_filter: Union[str, None] = None
|
||||
|
||||
def __init__(self, rights: rights.BaseRights, user: str, path: str
|
||||
def __init__(self, rights: rights.BaseRights, user: str, path: str, permissions_filter: Union[str, None] = None
|
||||
) -> None:
|
||||
self._rights = rights
|
||||
self.user = user
|
||||
self.path = path
|
||||
self.parent_path = pathutils.unstrip_path(
|
||||
posixpath.dirname(pathutils.strip_path(path)), True)
|
||||
self.parent_path = pathutils.parent_path(path)
|
||||
self.permissions = self._rights.authorization(self.user, self.path)
|
||||
if permissions_filter is not None:
|
||||
self._permissions_filter = permissions_filter
|
||||
permissions_filtered = intersect(self.permissions, permissions_filter)
|
||||
self.permissions = permissions_filtered
|
||||
self._parent_permissions = None
|
||||
|
||||
@property
|
||||
@@ -124,6 +128,9 @@ class Access:
|
||||
if self._parent_permissions is None:
|
||||
self._parent_permissions = self._rights.authorization(
|
||||
self.user, self.parent_path)
|
||||
if self._permissions_filter is not None:
|
||||
parent_permissions_filtered = intersect(self._parent_permissions, self._permissions_filter)
|
||||
self._parent_permissions = parent_permissions_filtered
|
||||
return self._parent_permissions
|
||||
|
||||
def check(self, permission: str,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Copyright © 2008 Pascal Halter
|
||||
# Copyright © 2008-2017 Guillaume Ayoub
|
||||
# Copyright © 2017-2020 Unrud <unrud@outlook.com>
|
||||
# Copyright © 2024-2025 Peter Bieringer <pb@bieringer.de>
|
||||
# Copyright © 2024-2026 Peter Bieringer <pb@bieringer.de>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -57,7 +57,8 @@ class ApplicationPartDelete(ApplicationBase):
|
||||
def do_DELETE(self, environ: types.WSGIEnviron, base_prefix: str,
|
||||
path: str, user: str, remote_host: str, remote_useragent: str) -> types.WSGIResponse:
|
||||
"""Manage DELETE request."""
|
||||
access = Access(self._rights, user, path)
|
||||
permissions_filter = None
|
||||
access = Access(self._rights, user, path, permissions_filter)
|
||||
if not access.check("w"):
|
||||
return httputils.NOT_ALLOWED
|
||||
with self._storage.acquire_lock("w", user, path=path, request="DELETE"):
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Copyright © 2008 Pascal Halter
|
||||
# Copyright © 2008-2017 Guillaume Ayoub
|
||||
# Copyright © 2017-2023 Unrud <unrud@outlook.com>
|
||||
# Copyright © 2025-2025 Peter Bieringer <pb@bieringer.de>
|
||||
# Copyright © 2025-2026 Peter Bieringer <pb@bieringer.de>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -76,7 +76,8 @@ class ApplicationPartGet(ApplicationBase):
|
||||
return httputils.redirect(location, client.MOVED_PERMANENTLY)
|
||||
# Dispatch /.web path to web module
|
||||
return self._web.get(environ, base_prefix, path, user)
|
||||
access = Access(self._rights, user, path)
|
||||
permissions_filter = None
|
||||
access = Access(self._rights, user, path, permissions_filter)
|
||||
if not access.check("r") and "i" not in access.permissions:
|
||||
return httputils.NOT_ALLOWED
|
||||
with self._storage.acquire_lock("r", user):
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Copyright © 2008 Pascal Halter
|
||||
# Copyright © 2008-2017 Guillaume Ayoub
|
||||
# Copyright © 2017-2021 Unrud <unrud@outlook.com>
|
||||
# Copyright © 2024-2025 Peter Bieringer <pb@bieringer.de>
|
||||
# Copyright © 2024-2026 Peter Bieringer <pb@bieringer.de>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -19,7 +19,6 @@
|
||||
# along with Radicale. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import errno
|
||||
import posixpath
|
||||
import re
|
||||
import socket
|
||||
from http import client
|
||||
@@ -62,8 +61,7 @@ class ApplicationPartMkcalendar(ApplicationBase):
|
||||
if item:
|
||||
return self._webdav_error_response(
|
||||
client.CONFLICT, "D:resource-must-be-null")
|
||||
parent_path = pathutils.unstrip_path(
|
||||
posixpath.dirname(pathutils.strip_path(path)), True)
|
||||
parent_path = pathutils.parent_path(path)
|
||||
parent_item = next(iter(self._storage.discover(parent_path)), None)
|
||||
if not parent_item:
|
||||
return httputils.CONFLICT
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Copyright © 2008 Pascal Halter
|
||||
# Copyright © 2008-2017 Guillaume Ayoub
|
||||
# Copyright © 2017-2021 Unrud <unrud@outlook.com>
|
||||
# Copyright © 2024-2025 Peter Bieringer <pb@bieringer.de>
|
||||
# Copyright © 2024-2026 Peter Bieringer <pb@bieringer.de>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -19,7 +19,6 @@
|
||||
# along with Radicale. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import errno
|
||||
import posixpath
|
||||
import re
|
||||
import socket
|
||||
from http import client
|
||||
@@ -66,8 +65,7 @@ class ApplicationPartMkcol(ApplicationBase):
|
||||
item = next(iter(self._storage.discover(path)), None)
|
||||
if item:
|
||||
return httputils.METHOD_NOT_ALLOWED
|
||||
parent_path = pathutils.unstrip_path(
|
||||
posixpath.dirname(pathutils.strip_path(path)), True)
|
||||
parent_path = pathutils.parent_path(path)
|
||||
parent_item = next(iter(self._storage.discover(parent_path)), None)
|
||||
if not parent_item:
|
||||
return httputils.CONFLICT
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Copyright © 2008 Pascal Halter
|
||||
# Copyright © 2008-2017 Guillaume Ayoub
|
||||
# Copyright © 2017-2023 Unrud <unrud@outlook.com>
|
||||
# Copyright © 2023-2025 Peter Bieringer <pb@bieringer.de>
|
||||
# Copyright © 2023-2026 Peter Bieringer <pb@bieringer.de>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -67,7 +67,8 @@ class ApplicationPartMove(ApplicationBase):
|
||||
# Remote destination server, not supported
|
||||
return httputils.REMOTE_DESTINATION
|
||||
|
||||
access = Access(self._rights, user, path)
|
||||
permissions_filter = None
|
||||
access = Access(self._rights, user, path, permissions_filter)
|
||||
if not access.check("w"):
|
||||
return httputils.NOT_ALLOWED
|
||||
to_path = pathutils.sanitize_path(to_url.path)
|
||||
@@ -76,7 +77,9 @@ class ApplicationPartMove(ApplicationBase):
|
||||
"start with base prefix", to_path, path)
|
||||
return httputils.NOT_ALLOWED
|
||||
to_path = to_path[len(base_prefix):]
|
||||
to_access = Access(self._rights, user, to_path)
|
||||
to_user = user
|
||||
to_permissions_filter = None
|
||||
to_access = Access(self._rights, to_user, to_path, to_permissions_filter)
|
||||
if not to_access.check("w"):
|
||||
return httputils.NOT_ALLOWED
|
||||
|
||||
@@ -94,8 +97,7 @@ class ApplicationPartMove(ApplicationBase):
|
||||
to_item = next(iter(self._storage.discover(to_path)), None)
|
||||
if isinstance(to_item, storage.BaseCollection):
|
||||
return httputils.FORBIDDEN
|
||||
to_parent_path = pathutils.unstrip_path(
|
||||
posixpath.dirname(pathutils.strip_path(to_path)), True)
|
||||
to_parent_path = pathutils.parent_path(to_path)
|
||||
to_collection = next(iter(
|
||||
self._storage.discover(to_parent_path)), None)
|
||||
if not to_collection:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Copyright © 2008 Pascal Halter
|
||||
# Copyright © 2008-2017 Guillaume Ayoub
|
||||
# Copyright © 2017-2021 Unrud <unrud@outlook.com>
|
||||
# Copyright © 2025-2025 Peter Bieringer <pb@bieringer.de>
|
||||
# Copyright © 2025-2026 Peter Bieringer <pb@bieringer.de>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -405,7 +405,9 @@ class ApplicationPartPropfind(ApplicationBase):
|
||||
def do_PROPFIND(self, environ: types.WSGIEnviron, base_prefix: str,
|
||||
path: str, user: str, remote_host: str, remote_useragent: str) -> types.WSGIResponse:
|
||||
"""Manage PROPFIND request."""
|
||||
access = Access(self._rights, user, path)
|
||||
http_depth = environ.get("HTTP_DEPTH", "0")
|
||||
permissions_filter = None
|
||||
access = Access(self._rights, user, path, permissions_filter)
|
||||
if not access.check("r"):
|
||||
return httputils.NOT_ALLOWED
|
||||
try:
|
||||
@@ -419,7 +421,7 @@ class ApplicationPartPropfind(ApplicationBase):
|
||||
return httputils.REQUEST_TIMEOUT
|
||||
with self._storage.acquire_lock("r", user):
|
||||
items_iter = iter(self._storage.discover(
|
||||
path, environ.get("HTTP_DEPTH", "0"),
|
||||
path, http_depth,
|
||||
None, self._rights._user_groups))
|
||||
# take root item for rights checking
|
||||
item = next(items_iter, None)
|
||||
@@ -429,11 +431,11 @@ class ApplicationPartPropfind(ApplicationBase):
|
||||
return httputils.NOT_ALLOWED
|
||||
# put item back
|
||||
items_iter = itertools.chain([item], items_iter)
|
||||
allowed_items = self._collect_allowed_items(items_iter, user)
|
||||
headers = {"DAV": httputils.DAV_HEADERS,
|
||||
"Content-Type": "text/xml; charset=%s" % self._encoding}
|
||||
xml_answer = xml_propfind(base_prefix, path, xml_content,
|
||||
allowed_items, user, self._encoding, max_resource_size=self._max_resource_size)
|
||||
if xml_answer is None:
|
||||
return httputils.NOT_ALLOWED
|
||||
return client.MULTI_STATUS, headers, self._xml_response(xml_answer), xmlutils.pretty_xml(xml_content)
|
||||
allowed_items = list(self._collect_allowed_items(items_iter, user))
|
||||
headers = {"DAV": httputils.DAV_HEADERS,
|
||||
"Content-Type": "text/xml; charset=%s" % self._encoding}
|
||||
xml_answer = xml_propfind(base_prefix, path, xml_content,
|
||||
allowed_items, user, self._encoding, max_resource_size=self._max_resource_size)
|
||||
if xml_answer is None:
|
||||
return httputils.NOT_ALLOWED
|
||||
return client.MULTI_STATUS, headers, self._xml_response(xml_answer), xmlutils.pretty_xml(xml_content)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# Copyright © 2008-2017 Guillaume Ayoub
|
||||
# Copyright © 2017-2020 Unrud <unrud@outlook.com>
|
||||
# Copyright © 2020-2020 Tuna Celik <tuna@jakpark.com>
|
||||
# Copyright © 2025-2025 Peter Bieringer <pb@bieringer.de>
|
||||
# Copyright © 2025-2026 Peter Bieringer <pb@bieringer.de>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -75,7 +75,8 @@ class ApplicationPartProppatch(ApplicationBase):
|
||||
def do_PROPPATCH(self, environ: types.WSGIEnviron, base_prefix: str,
|
||||
path: str, user: str, remote_host: str, remote_useragent: str) -> types.WSGIResponse:
|
||||
"""Manage PROPPATCH request."""
|
||||
access = Access(self._rights, user, path)
|
||||
permissions_filter = None
|
||||
access = Access(self._rights, user, path, permissions_filter)
|
||||
if not access.check("w"):
|
||||
return httputils.NOT_ALLOWED
|
||||
try:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# Copyright © 2024-2024 Pieter Hijma <pieterhijma@users.noreply.github.com>
|
||||
# Copyright © 2024-2024 Ray <ray@react0r.com>
|
||||
# Copyright © 2024-2025 Georgiy <metallerok@gmail.com>
|
||||
# Copyright © 2024-2025 Peter Bieringer <pb@bieringer.de>
|
||||
# Copyright © 2024-2026 Peter Bieringer <pb@bieringer.de>
|
||||
# Copyright © 2025-2025 David Greaves <david@dgreaves.com>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
@@ -810,7 +810,8 @@ class ApplicationPartReport(ApplicationBase):
|
||||
def do_REPORT(self, environ: types.WSGIEnviron, base_prefix: str,
|
||||
path: str, user: str, remote_host: str, remote_useragent: str) -> types.WSGIResponse:
|
||||
"""Manage REPORT request."""
|
||||
access = Access(self._rights, user, path)
|
||||
permissions_filter = None
|
||||
access = Access(self._rights, user, path, permissions_filter)
|
||||
if not access.check("r"):
|
||||
return httputils.NOT_ALLOWED
|
||||
try:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Copyright © 2014 Jean-Marc Martins
|
||||
# Copyright © 2012-2017 Guillaume Ayoub
|
||||
# Copyright © 2017-2022 Unrud <unrud@outlook.com>
|
||||
# Copyright © 2025-2025 Peter Bieringer <pb@bieringer.de>
|
||||
# Copyright © 2025-2026 Peter Bieringer <pb@bieringer.de>
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -293,6 +293,10 @@ def path_to_filesystem(root: str, sane_path: str) -> str:
|
||||
return safe_path
|
||||
|
||||
|
||||
def parent_path(path: str) -> str:
|
||||
return unstrip_path(posixpath.dirname(strip_path(path)), True)
|
||||
|
||||
|
||||
class UnsafePathError(ValueError):
|
||||
|
||||
def __init__(self, path: str) -> None:
|
||||
|
||||
Reference in New Issue
Block a user