From d22d74320a24805900897b586c8f09603da465dc Mon Sep 17 00:00:00 2001 From: Juan F Arjona Date: Fri, 2 May 2025 18:02:43 -0400 Subject: [PATCH 1/5] Added urldecode_username option to decode the username when sent by the client. This solved problems when authenticating against an imap server from the iOS calendar. --- radicale/auth/__init__.py | 6 ++++++ radicale/config.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/radicale/auth/__init__.py b/radicale/auth/__init__.py index 43ce953b..be96334e 100644 --- a/radicale/auth/__init__.py +++ b/radicale/auth/__init__.py @@ -37,6 +37,7 @@ from typing import List, Sequence, Set, Tuple, Union, final from radicale import config, types, utils from radicale.log import logger +from urllib.parse import unquote INTERNAL_TYPES: Sequence[str] = ("none", "remote_user", "http_x_remote_user", "denyall", @@ -93,6 +94,7 @@ def load(configuration: "config.Configuration") -> "BaseAuth": class BaseAuth: _ldap_groups: Set[str] = set([]) + _urldecode_username: bool _lc_username: bool _uc_username: bool _strip_domain: bool @@ -119,9 +121,11 @@ class BaseAuth: self._lc_username = configuration.get("auth", "lc_username") self._uc_username = configuration.get("auth", "uc_username") self._strip_domain = configuration.get("auth", "strip_domain") + self._urldecode_username = configuration.get("auth", "urldecode_username") logger.info("auth.strip_domain: %s", self._strip_domain) logger.info("auth.lc_username: %s", self._lc_username) logger.info("auth.uc_username: %s", self._uc_username) + logger.info("auth.urldecode_username: %s", self._urldecode_username) if self._lc_username is True and self._uc_username is True: raise RuntimeError("auth.lc_username and auth.uc_username cannot be enabled together") self._auth_delay = configuration.get("auth", "delay") @@ -219,6 +223,8 @@ class BaseAuth: login = login.lower() if self._uc_username: login = login.upper() + if self._urldecode_username: + login = unquote(login) if self._strip_domain: login = login.split('@')[0] if self._cache_logins is True: diff --git a/radicale/config.py b/radicale/config.py index cb2285c3..c4f5fbe7 100644 --- a/radicale/config.py +++ b/radicale/config.py @@ -342,6 +342,10 @@ DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([ ("lc_username", { "value": "False", "help": "convert username to lowercase, must be true for case-insensitive auth providers", + "type": bool}), + ("urldecode_username", { + "value": "False", + "help": "url-decode the username, set to True when clients send url-encoded email address as username", "type": bool})])), ("rights", OrderedDict([ ("type", { From f0aa588638d04605757db62fae34b335053c5406 Mon Sep 17 00:00:00 2001 From: Juan F Arjona Date: Mon, 5 May 2025 15:20:38 -0500 Subject: [PATCH 2/5] Update config Added URL-Decode option --- config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config b/config index bbeb19d1..8f536958 100644 --- a/config +++ b/config @@ -184,6 +184,8 @@ # Permit overwrite of a collection (global) #permit_overwrite_collection = True +# URL Decode the given username (when URL-encoded by the client - useful for iOS devices when using email address) +# urldecode_username = False [storage] From dd2552725221e115ccfc1b7741f8d0cc1db5b1d8 Mon Sep 17 00:00:00 2001 From: Juan F Arjona Date: Mon, 5 May 2025 15:25:56 -0500 Subject: [PATCH 3/5] Update DOCUMENTATION.md --- DOCUMENTATION.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index bbfac0a2..f5d8aad5 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -1187,6 +1187,16 @@ Strip domain from username Default: `False` +##### urldecode_username + +_(>= 3.5.3)_ + +URL Decode the username. When the username is an email, some clients send the username URL-encoded (notably iOS devices) +breaking the authentication process (user@example.com becomes user%40example.com). This setting will force decoding the username. + +Default: `False` + + #### rights ##### type From 1fe0211ba60097caaacf9da50d504465f47e9cb2 Mon Sep 17 00:00:00 2001 From: Juan F Arjona Date: Wed, 7 May 2025 11:59:27 -0500 Subject: [PATCH 4/5] Update __init__.py Making lint happy (?) --- radicale/auth/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radicale/auth/__init__.py b/radicale/auth/__init__.py index be96334e..05d2e41d 100644 --- a/radicale/auth/__init__.py +++ b/radicale/auth/__init__.py @@ -34,10 +34,11 @@ import os import threading import time from typing import List, Sequence, Set, Tuple, Union, final +from urllib.parse import unquote from radicale import config, types, utils from radicale.log import logger -from urllib.parse import unquote + INTERNAL_TYPES: Sequence[str] = ("none", "remote_user", "http_x_remote_user", "denyall", From 7eb4e037cc42bebab476385cffcec3038c5883f7 Mon Sep 17 00:00:00 2001 From: Juan F Arjona Date: Wed, 7 May 2025 13:33:18 -0400 Subject: [PATCH 5/5] Fixing extra space lint doesn't like :-|. --- radicale/auth/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/radicale/auth/__init__.py b/radicale/auth/__init__.py index 05d2e41d..2de8c4e9 100644 --- a/radicale/auth/__init__.py +++ b/radicale/auth/__init__.py @@ -39,7 +39,6 @@ from urllib.parse import unquote from radicale import config, types, utils from radicale.log import logger - INTERNAL_TYPES: Sequence[str] = ("none", "remote_user", "http_x_remote_user", "denyall", "htpasswd",