diff --git a/config b/config index 0d17dae..bd35574 100644 --- a/config +++ b/config @@ -66,7 +66,7 @@ [auth] # Authentication method -# Value: None | htpasswd +# Value: None | htpasswd | remote_user | http_x_remote_user #type = None # Htpasswd filename diff --git a/radicale/__init__.py b/radicale/__init__.py index 9b85c6b..5137434 100644 --- a/radicale/__init__.py +++ b/radicale/__init__.py @@ -371,15 +371,19 @@ class Application: function = getattr(self, "do_%s" % environ["REQUEST_METHOD"].upper()) # Ask authentication backend to check rights - authorization = environ.get("HTTP_AUTHORIZATION", None) - if authorization and authorization.startswith("Basic"): + external_login = self.Auth.get_external_login(environ) + authorization = environ.get("HTTP_AUTHORIZATION", "") + if external_login: + login, password = external_login + elif authorization.startswith("Basic"): authorization = authorization[len("Basic"):].strip() login, password = self.decode(base64.b64decode( authorization.encode("ascii")), environ).split(":", 1) - user = self.Auth.map_login_to_user(login) else: - user = self.Auth.map_login_to_user(environ.get("REMOTE_USER", "")) + # DEPRECATED: use remote_user backend instead + login = environ.get("REMOTE_USER", "") password = "" + user = self.Auth.map_login_to_user(login) # If "/.well-known" is not available, clients query "/" if path == "/.well-known" or path.startswith("/.well-known/"): @@ -437,7 +441,7 @@ class Application: status, headers, answer = NOT_ALLOWED if (status, headers, answer) == NOT_ALLOWED and not ( - user and is_authenticated): + 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 b202c5d..06f15f1 100644 --- a/radicale/auth.py +++ b/radicale/auth.py @@ -67,6 +67,10 @@ def load(configuration, logger): logger.debug("Authentication type is %s", auth_type) if auth_type == "None": class_ = NoneAuth + elif auth_type == "remote_user": + class_ = RemoteUserAuth + elif auth_type == "http_x_remote_user": + class_ = HttpXRemoteUserAuth elif auth_type == "htpasswd": class_ = Auth else: @@ -79,6 +83,14 @@ class BaseAuth: self.configuration = configuration self.logger = logger + def get_external_login(self, environ): + """Optionally provide the login and password externally. + + Returns a tuple (login, password) or (). + + """ + return () + def is_authenticated(self, user, password): """Validate credentials. @@ -201,3 +213,13 @@ class Auth(BaseAuth): if login_ok & password_ok: return True return False + + +class RemoteUserAuth(NoneAuth): + def get_external_login(self, environ): + return environ.get("REMOTE_USER", ""), "" + + +class HttpXRemoteUserAuth(NoneAuth): + def get_external_login(self, environ): + return environ.get("HTTP_X_REMOTE_USER", ""), ""