diff --git a/radicale/__init__.py b/radicale/__init__.py index 5c490e5..b148b49 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -449,6 +449,7 @@ class Application: return response(*NOT_FOUND) # Ask authentication backend to check rights + login = password = "" external_login = self.Auth.get_external_login(environ) authorization = environ.get("HTTP_AUTHORIZATION", "") if external_login: @@ -458,10 +459,6 @@ class Application: authorization = authorization[len("Basic"):].strip() login, password = self.decode(base64.b64decode( authorization.encode("ascii")), environ).split(":", 1) - else: - # DEPRECATED: use remote_user backend instead - login = environ.get("REMOTE_USER", "") - password = "" user = self.Auth.login(login, password) or "" if login else "" if user and login == user: @@ -961,7 +958,7 @@ class Application: new_props = parent_item.get_meta() new_props["tag"] = tag storage.check_and_sanitize_props(new_props) - parent_item.set_meta_all(new_props) + parent_item.set_meta(new_props) new_item = parent_item.upload(href, items[0]) except ValueError as e: logger.warning( diff --git a/radicale/auth.py b/radicale/auth.py index 119cdad..ab73a98 100644 --- a/radicale/auth.py +++ b/radicale/auth.py @@ -62,14 +62,13 @@ from importlib import import_module from radicale.log import logger -INTERNAL_TYPES = ("None", "none", "remote_user", "http_x_remote_user", - "htpasswd") +INTERNAL_TYPES = ("none", "remote_user", "http_x_remote_user", "htpasswd") def load(configuration): """Load the authentication manager chosen in configuration.""" auth_type = configuration.get("auth", "type") - if auth_type in ("None", "none"): # DEPRECATED: use "none" + if auth_type == "none": class_ = NoneAuth elif auth_type == "remote_user": class_ = RemoteUserAuth @@ -114,39 +113,12 @@ class BaseAuth: """ - user = self.map_login_to_user(login) - if user and self.is_authenticated2(login, user, password): - return user - return "" - - def is_authenticated2(self, login, user, password): - """Validate credentials. - - DEPRECATED: use ``login`` instead - - """ - return self.is_authenticated(user, password) - - def is_authenticated(self, user, password): - """Validate credentials. - - DEPRECATED: use ``login`` instead - - """ raise NotImplementedError - def map_login_to_user(self, login): - """Map login name to internal user. - - DEPRECATED: use ``login`` instead - - """ - return login - class NoneAuth(BaseAuth): - def is_authenticated(self, user, password): - return True + def login(self, login, password): + return login class Auth(BaseAuth): @@ -239,11 +211,11 @@ class Auth(BaseAuth): hash_value = hash_value.strip() return md5_apr1.verify(password, hash_value) - def is_authenticated(self, user, password): + def login(self, login, password): """Validate credentials. - Iterate through htpasswd credential file until user matches, extract - hash (encrypted password) and check hash against user-given password, + Iterate through htpasswd credential file until login matches, extract + hash (encrypted password) and check hash against password, using the method specified in the Radicale config. The content of the file is not cached because reading is generally a @@ -257,20 +229,21 @@ class Auth(BaseAuth): line = line.rstrip("\n") if line.lstrip() and not line.lstrip().startswith("#"): try: - login, hash_value = line.split(":", maxsplit=1) + hash_login, hash_value = line.split( + ":", maxsplit=1) # Always compare both login and password to avoid # timing attacks, see #591. - login_ok = hmac.compare_digest(login, user) + login_ok = hmac.compare_digest(hash_login, login) password_ok = self.verify(hash_value, password) if login_ok and password_ok: - return True + return login except ValueError as e: raise RuntimeError("Invalid htpasswd file %r: %s" % (self.filename, e)) from e except OSError as e: raise RuntimeError("Failed to load htpasswd file %r: %s" % (self.filename, e)) from e - return False + return "" class RemoteUserAuth(NoneAuth): diff --git a/radicale/rights.py b/radicale/rights.py index 6881c3d..b5c3df3 100644 --- a/radicale/rights.py +++ b/radicale/rights.py @@ -46,16 +46,16 @@ from importlib import import_module from radicale import storage from radicale.log import logger -INTERNAL_TYPES = ("None", "none", "authenticated", "owner_write", "owner_only", +INTERNAL_TYPES = ("none", "authenticated", "owner_write", "owner_only", "from_file") def load(configuration): """Load the rights manager chosen in configuration.""" rights_type = configuration.get("rights", "type") - if configuration.get("auth", "type") in ("None", "none"): # DEPRECATED - rights_type = "None" - if rights_type in ("None", "none"): # DEPRECATED: use "none" + if configuration.get("auth", "type") == "none": + rights_type = "none" + if rights_type == "none": rights_class = NoneRights elif rights_type == "authenticated": rights_class = AuthenticatedRights diff --git a/radicale/storage.py b/radicale/storage.py index 2ee4b18..9701b38 100644 --- a/radicale/storage.py +++ b/radicale/storage.py @@ -495,16 +495,6 @@ class BaseCollection: """Collection is a principal.""" return bool(self.path) and "/" not in self.path - @owner.setter - def owner(self, value): - # DEPRECATED: Included for compatibility reasons - pass - - @is_principal.setter - def is_principal(self, value): - # DEPRECATED: Included for compatibility reasons - pass - @classmethod def discover(cls, path, depth="0"): """Discover a list of collections under the given ``path``. @@ -596,14 +586,6 @@ class BaseCollection: raise NotImplementedError def get_multi(self, hrefs): - """Fetch multiple items. Duplicate hrefs must be ignored. - - DEPRECATED: use ``get_multi2`` instead - - """ - return (self.get(href) for href in set(hrefs)) - - def get_multi2(self, hrefs): """Fetch multiple items. Functionally similar to ``get``, but might bring performance benefits @@ -639,14 +621,6 @@ class BaseCollection: """ return ((item, False) for item in self.get_all()) - def pre_filtered_list(self, filters): - """List collection items with optional pre filtering. - - DEPRECATED: use ``get_all_filtered`` instead - - """ - return self.get_all() - def has(self, href): """Check if an item exists by its href. @@ -687,26 +661,10 @@ class BaseCollection: def set_meta(self, props): """Set metadata values for collection. - ``props`` a dict with updates for properties. If a value is empty, the - property must be deleted. - - DEPRECATED: use ``set_meta_all`` instead - - """ - raise NotImplementedError - - def set_meta_all(self, props): - """Set metadata values for collection. - ``props`` a dict with values for properties. """ - delta_props = self.get_meta() - for key in delta_props.keys(): - if key not in props: - delta_props[key] = None - delta_props.update(props) - self.set_meta(self, delta_props) + raise NotImplementedError @property def last_modified(self): @@ -808,16 +766,11 @@ class Collection(BaseCollection): lock_path = os.path.join(folder, ".Radicale.lock") cls._lock = FileBackedRwLock(lock_path) - def __init__(self, path, principal=None, folder=None, - filesystem_path=None): - # DEPRECATED: Remove principal and folder attributes - if folder is None: - folder = self._get_collection_root_folder() + def __init__(self, path, filesystem_path=None): + folder = self._get_collection_root_folder() # Path should already be sanitized self.path = sanitize_path(path).strip("/") self._encoding = self.configuration.get("encoding", "stock") - # DEPRECATED: Use ``self._encoding`` instead - self.encoding = self._encoding if filesystem_path is None: filesystem_path = path_to_filesystem(folder, self.path) self._filesystem_path = filesystem_path @@ -1028,7 +981,7 @@ class Collection(BaseCollection): tmp_filesystem_path = os.path.join(tmp_dir, "collection") os.makedirs(tmp_filesystem_path) self = cls(sane_path, filesystem_path=tmp_filesystem_path) - self.set_meta_all(props) + self.set_meta(props) if collection: if props.get("tag") == "VCALENDAR": @@ -1475,7 +1428,7 @@ class Collection(BaseCollection): text=text, item=vobject_item, uid=uid, name=name, component_name=tag), (tag, start, end) - def get_multi2(self, hrefs): + def get_multi(self, hrefs): # It's faster to check for file name collissions here, because # we only need to call os.listdir once. files = None @@ -1575,7 +1528,7 @@ class Collection(BaseCollection): "%r: %s" % (self.path, e)) from e return self._meta_cache.get(key) if key else self._meta_cache - def set_meta_all(self, props): + def set_meta(self, props): with self._atomic_write(self._props_path, "w") as f: json.dump(props, f, sort_keys=True) diff --git a/radicale/tests/custom/auth.py b/radicale/tests/custom/auth.py index c61f637..b5ced62 100644 --- a/radicale/tests/custom/auth.py +++ b/radicale/tests/custom/auth.py @@ -27,5 +27,7 @@ from radicale import auth class Auth(auth.BaseAuth): - def is_authenticated(self, user, password): - return user == "tmp" + def login(self, login, password): + if login == "tmp": + return login + return "" diff --git a/radicale/web.py b/radicale/web.py index 74dba9f..2f9602d 100644 --- a/radicale/web.py +++ b/radicale/web.py @@ -45,13 +45,13 @@ MIMETYPES = { ".xml": "text/xml"} FALLBACK_MIMETYPE = "application/octet-stream" -INTERNAL_TYPES = ("None", "none", "internal") +INTERNAL_TYPES = ("none", "internal") def load(configuration): """Load the web module chosen in configuration.""" web_type = configuration.get("web", "type") - if web_type in ("None", "none"): # DEPRECATED: use "none" + if web_type == "none": web_class = NoneWeb elif web_type == "internal": web_class = Web diff --git a/radicale/xmlutils.py b/radicale/xmlutils.py index d7dac0a..d51a17f 100644 --- a/radicale/xmlutils.py +++ b/radicale/xmlutils.py @@ -1121,7 +1121,7 @@ def proppatch(base_prefix, path, xml_request, collection): pass _add_propstat_to(response, short_name, 200) storage.check_and_sanitize_props(new_props) - collection.set_meta_all(new_props) + collection.set_meta(new_props) return multistatus @@ -1229,7 +1229,7 @@ def report(base_prefix, path, xml_request, collection): # Reference is a collection collection_requested = True - for name, item in collection.get_multi2(get_names()): + for name, item in collection.get_multi(get_names()): if not item: uri = "/" + posixpath.join(collection.path, name) response = _item_response(base_prefix, uri,