diff --git a/radicale/__init__.py b/radicale/__init__.py index c6e97bb..b3f6db1 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -440,7 +440,6 @@ class Application: authorization = environ.get("HTTP_AUTHORIZATION", "") if external_login: login, password = external_login - login, password = login or "", password or "" elif authorization.startswith("Basic"): authorization = authorization[len("Basic"):].strip() login, password = self.decode(base64.b64decode( @@ -449,28 +448,30 @@ class Application: # DEPRECATED: use remote_user backend instead login = environ.get("REMOTE_USER", "") password = "" + user = self.Auth.map_login_to_user(login) - user = self.Auth.login(login, password) or "" if login else "" - if user and login == user: - self.logger.info("Successful login: %r", user) - elif user: - self.logger.info("Successful login: %r -> %r", login, user) - elif login: - self.logger.info("Failed login attempt: %r", login) - # Random delay to avoid timing oracles and bruteforce attacks - delay = self.configuration.getfloat("auth", "delay") - if delay > 0: - random_delay = delay * (0.5 + random.random()) - self.logger.debug("Sleeping %.3f seconds", random_delay) - time.sleep(random_delay) - - if user and not storage.is_safe_path_component(user): + if not user: + is_authenticated = True + elif not storage.is_safe_path_component(user): # Prevent usernames like "user/calendar.ics" self.logger.info("Refused unsafe username: %r", user) - user = "" + is_authenticated = False + else: + is_authenticated = self.Auth.is_authenticated2(login, user, + password) + if not is_authenticated: + self.logger.info("Failed login attempt: %r", user) + # Random delay to avoid timing oracles and bruteforce attacks + delay = self.configuration.getfloat("auth", "delay") + if delay > 0: + random_delay = delay * (0.5 + random.random()) + self.logger.debug("Sleeping %.3f seconds", random_delay) + time.sleep(random_delay) + else: + self.logger.info("Successful login: %r", user) # Create principal collection - if user: + if user and is_authenticated: principal_path = "/%s/" % user if self.Rights.authorized(user, principal_path, "w"): with self.Collection.acquire_lock("r", user): @@ -484,7 +485,7 @@ class Application: except ValueError as e: self.logger.warning("Failed to create principal " "collection %r: %s", user, e) - user = "" + is_authenticated = False else: self.logger.warning("Access to principal path %r denied by " "rights backend", principal_path) @@ -499,7 +500,7 @@ class Application: "Request body too large: %d", content_length) return response(*REQUEST_ENTITY_TOO_LARGE) - if not login or user: + if is_authenticated: status, headers, answer = function( environ, base_prefix, path, user) if (status, headers, answer) == NOT_ALLOWED: @@ -508,8 +509,8 @@ class Application: else: status, headers, answer = NOT_ALLOWED - if ((status, headers, answer) == NOT_ALLOWED and not user and - not external_login): + if (status, headers, answer) == NOT_ALLOWED and not ( + user and is_authenticated) and not external_login: # Unknown or unauthorized user self.logger.debug("Asking client for authentication") status = client.UNAUTHORIZED diff --git a/radicale/auth.py b/radicale/auth.py index 16704bb..fc5d425 100644 --- a/radicale/auth.py +++ b/radicale/auth.py @@ -102,26 +102,14 @@ class BaseAuth: """ return () - def login(self, login, password): - """Check credentials and map login to internal user - - ``login`` the login name - - ``password`` the password - - Returns the user name or ``""`` for invalid credentials. - - """ - - 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 + ``login`` the login name + + ``user`` the user from ``map_login_to_user(login)``. + + ``password`` the login password """ return self.is_authenticated(user, password) @@ -129,7 +117,7 @@ class BaseAuth: def is_authenticated(self, user, password): """Validate credentials. - DEPRECATED: use ``login`` instead + DEPRECATED: use ``is_authenticated2`` instead """ raise NotImplementedError @@ -137,7 +125,11 @@ class BaseAuth: def map_login_to_user(self, login): """Map login name to internal user. - DEPRECATED: use ``login`` instead + ``login`` the login name, ``""`` for anonymous users + + Returns a string with the user name. + If a login can't be mapped to an user, return ``login`` and + return ``False`` in ``is_authenticated2(...)``. """ return login