From 5167f12624fac61b0226a61a527856e3a1637e6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dipl=2E=20Ing=2E=20P=C3=A9ter=20Varkoly?= Date: Mon, 26 Aug 2024 11:21:53 +0200 Subject: [PATCH] Rebase rights/from_file.py. Apply proposed/asked changes. --- config | 18 +++++----- radicale/app/__init__.py | 11 +++--- radicale/rights/from_file.py | 65 +++++++++++++++--------------------- rights | 8 ++--- 4 files changed, 45 insertions(+), 57 deletions(-) diff --git a/config b/config index 9ffbd72f..24e4c845 100644 --- a/config +++ b/config @@ -14,8 +14,6 @@ # CalDAV server hostnames separated by a comma # IPv4 syntax: address:port # IPv6 syntax: [address]:port -# For example: 0.0.0.0:9999, [::]:9999 -hosts = 0.0.0.0:5232 # Hostname syntax (using "getaddrinfo" to resolve to IPv4/IPv6 adress(es)): hostname:port # For example: 0.0.0.0:9999, [::]:9999, localhost:9999 #hosts = localhost:5232 @@ -55,23 +53,23 @@ hosts = 0.0.0.0:5232 [auth] # Authentication method -# Value: none | htpasswd | remote_user | http_x_remote_user -type = ldap +# Value: none | htpasswd | remote_user | http_x_remote_user | ldap +#type = ldap # URI to the LDAP server -ldap_uri = ldap://localhost +#ldap_uri = ldap://localhost # The base DN of the LDAP server -ldap_base = ##BASE_DN## +#ldap_base = ##BASE_DN## # The reader DN of the LDAP server -ldap_reader_dn = CN=ossreader,CN=Users,##BASE_DN## +#ldap_reader_dn = CN=ldapreader,CN=Users,##BASE_DN## # Password of the reader DN -ldap_secret = ossreader +#ldap_secret = ldapreader-secret # If the ldap groups of the user need to be loaded -ldap_load_groups = True +#ldap_load_groups = True # Value: none | htpasswd | remote_user | http_x_remote_user | denyall #type = none @@ -103,7 +101,7 @@ ldap_load_groups = True #type = owner_only # File for rights management from_file -file = /etc/radicale/rights +#file = /etc/radicale/rights # Permit delete of a collection (global) #permit_delete_collection = True diff --git a/radicale/app/__init__.py b/radicale/app/__init__.py index bdb70772..f1b5144f 100644 --- a/radicale/app/__init__.py +++ b/radicale/app/__init__.py @@ -250,11 +250,12 @@ class Application(ApplicationPartDelete, ApplicationPartHead, authorization.encode("ascii"))).split(":", 1) user = self._auth.login(login, password) or "" if login else "" - try: - logger.debug("Groups %r",",".join(self._auth._ldap_groups)) - self._rights._user_groups = self._auth._ldap_groups - except AttributeError: - pass + if self.configuration.get("auth", "type") == "ldap": + try: + logger.debug("Groups %r",",".join(self._auth._ldap_groups)) + self._rights._user_groups = self._auth._ldap_groups + except AttributeError: + pass if user and login == user: logger.info("Successful login: %r", user) elif user: diff --git a/radicale/rights/from_file.py b/radicale/rights/from_file.py index 74651a18..d8e287af 100644 --- a/radicale/rights/from_file.py +++ b/radicale/rights/from_file.py @@ -34,7 +34,7 @@ Leading or ending slashes are trimmed from collection's path. """ -from configparser import ConfigParser +import configparser import re from radicale import config, pathutils, rights @@ -44,66 +44,55 @@ from radicale.log import logger class Rights(rights.BaseRights): _filename: str - _rights_config: ConfigParser - _user_groups: set def __init__(self, configuration: config.Configuration) -> None: super().__init__(configuration) self._filename = configuration.get("rights", "file") - self._rights_config = ConfigParser() - try: - with open(self._filename, "r") as f: - self._rights_config.read_file(f) - logger.debug("Rights were read") - except Exception as e: - raise RuntimeError("Failed to load rights file %r: %s" % - (self._filename, e)) from e def authorization(self, user: str, path: str) -> str: user = user or "" sane_path = pathutils.strip_path(path) # Prevent "regex injection" escaped_user = re.escape(user) - logger.debug("authorization called %r %r",user,path) - - for section in self._rights_config.sections(): - group_match = [] + rights_config = configparser.ConfigParser() + try: + with open(self._filename, "r") as f: + rights_config.read_file(f) + except Exception as e: + raise RuntimeError("Failed to load rights file %r: %s" % + (self._filename, e)) from e + for section in rights_config.sections(): + group_match = False try: - collection_pattern = self._rights_config.get(section, "collection") - user_pattern = self._rights_config.get(section, "user", fallback = "") - allowed_groups = self._rights_config.get(section, "groups", fallback = "").split(",") + user_pattern = rights_config.get(section, "user") + collection_pattern = rights_config.get(section, "collection") + allowed_groups = rights_config.get(section, "groups", fallback = "").split(",") try: - group_match = self._user_groups.intersection(allowed_groups) - logger.debug("Groups %r, %r",",".join(group_match),";".join(groups)) + group_match = self._user_groups.intersection(allowed_groups) > 0 except: pass - # Use empty format() for harmonized handling of curly braces user_match = re.fullmatch(user_pattern.format(), user) - u_collection_match = user_match and re.fullmatch( + user_collection_match = user_match and re.fullmatch( collection_pattern.format( *(re.escape(s) for s in user_match.groups()), user=escaped_user), sane_path) - g_collection_match = re.fullmatch( collection_pattern.format(user=escaped_user), sane_path) + group_collection_match = re.fullmatch(collection_pattern.format(user=escaped_user), sane_path) except Exception as e: raise RuntimeError("Error in section %r of rights file %r: " "%s" % (section, self._filename, e)) from e - if user_match and u_collection_match: - logger.debug("User rule %r:%r matches %r:%r from section %r", + if user_match and user_collection_match: + permission = rights_config.get(section, "permissions") + logger.debug("Rule %r:%r matches %r:%r from section %r permission %r", user, sane_path, user_pattern, - collection_pattern, section) - return self._rights_config.get(section, "permissions") - if len(group_match) > 0 and g_collection_match: - logger.debug("Group rule %r:%r matches %r from section %r", - group_match, sane_path, - collection_pattern, section) - return self._rights_config.get(section, "permissions") -#if user_match and collection_match: -# permission = rights_config.get(section, "permissions") -# logger.debug("Rule %r:%r matches %r:%r from section %r permission %r", -# user, sane_path, user_pattern, -# collection_pattern, section, permission) -# return permission + collection_pattern, section, permission) + return permission + if group_match and group_collection_match: + permission = rights_config.get(section, "permissions") + logger.debug("Rule %r:%r matches %r:%r from section %r permission %r by group membership", + user, sane_path, user_pattern, + collection_pattern, section, permission) + return permission logger.debug("Rule %r:%r doesn't match %r:%r from section %r", user, sane_path, user_pattern, collection_pattern, section) diff --git a/rights b/rights index 03a05e80..d0589594 100644 --- a/rights +++ b/rights @@ -25,10 +25,10 @@ permissions: rw # Allow reading calendars and address books that are direct # children of the principal collection for other users -[calendarsReader] -user: .+ -collection: {user}/[^/]+ -permissions: r +#[calendarsReader] +#user: .+ +#collection: {user}/[^/]+ +#permissions: r # Rights management file for Radicale - A simple calendar server #