Kaynağa Gözat

add support for http_remote_user

Peter Bieringer 3 ay önce
ebeveyn
işleme
bf8619a41c

+ 8 - 1
DOCUMENTATION.md

@@ -902,8 +902,15 @@ Available types are:
   Requires validation, otherwise clients can supply the header themselves,
   which then is unconditionally trusted.
 
+* `http_remote_user` _(>= 3.5.9)_
+  Takes the username from the Remote-User HTTP header `HTTP_REMOTE_USER` and disables
+  Radicale's internal HTTP authentication. This can be used to provide the
+  username from a reverse proxy which authenticated the client upfront.
+  Requires validation, otherwise clients can supply the header themselves,
+  which then is unconditionally trusted.
+
 * `http_x_remote_user`  
-  Takes the username from the `X-Remote-User` HTTP header and disables
+  Takes the username from the X-Remote-User HTTP header `HTTP_X_REMOTE_USER` and disables
   Radicale's internal HTTP authentication. This can be used to provide the
   username from a reverse proxy which authenticated the client upfront.
   Requires validation, otherwise clients can supply the header themselves,

+ 1 - 1
config

@@ -63,7 +63,7 @@
 [auth]
 
 # Authentication method
-# Value: none | htpasswd | remote_user | http_x_remote_user | dovecot | ldap | oauth2 | pam | denyall
+# Value: none | htpasswd | remote_user | http_remote_user | http_x_remote_user | dovecot | ldap | oauth2 | pam | denyall
 #type = denyall
 
 # Cache logins for until expiration time

+ 1 - 1
radicale/app/__init__.py

@@ -275,7 +275,7 @@ class Application(ApplicationPartDelete, ApplicationPartHead,
                 logger.debug("Called by reverse proxy, remove base prefix %r from path: %r => %r", base_prefix, path, path_new)
                 path = path_new
             else:
-                if self._auth_type in ['remote_user', 'http_x_remote_user'] and self._web_type == 'internal':
+                if self._auth_type in ['remote_user', 'http_remote_user', 'http_x_remote_user'] and self._web_type == 'internal':
                     logger.warning("Called by reverse proxy, cannot remove base prefix %r from path: %r as not matching (may cause authentication issues using internal WebUI)", base_prefix, path)
                 else:
                     logger.debug("Called by reverse proxy, cannot remove base prefix %r from path: %r as not matching", base_prefix, path)

+ 3 - 1
radicale/auth/__init__.py

@@ -23,7 +23,7 @@ Authentication module.
 
 Authentication is based on usernames and passwords. If something more
 advanced is needed an external WSGI server or reverse proxy can be used
-(see ``remote_user`` or ``http_x_remote_user`` backend).
+(see ``remote_user``, ``http_remote_user`` or ``http_x_remote_user`` backend).
 
 Take a look at the class ``BaseAuth`` if you want to implement your own.
 
@@ -40,6 +40,7 @@ from radicale import config, types, utils
 from radicale.log import logger
 
 INTERNAL_TYPES: Sequence[str] = ("none", "remote_user", "http_x_remote_user",
+                                 "http_remote_user",
                                  "denyall",
                                  "htpasswd",
                                  "ldap",
@@ -59,6 +60,7 @@ CACHE_LOGIN_TYPES: Sequence[str] = (
 
 INSECURE_IF_NO_LOOPBACK_TYPES: Sequence[str] = (
                                     "remote_user",
+                                    "http_remote_user",
                                     "http_x_remote_user",
                                    )
 

+ 36 - 0
radicale/auth/http_remote_user.py

@@ -0,0 +1,36 @@
+# This file is part of Radicale - CalDAV and CardDAV server
+# Copyright © 2025-2025 Peter Bieringer <pb@bieringer.de>
+#
+# This library is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Radicale.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Authentication backend that takes the username from the
+``HTTP_REMOTE_USER`` header.
+
+It's intended for use with a reverse proxy. Be aware as this will be insecure
+if the reverse proxy is not configured properly.
+
+"""
+
+from typing import Tuple, Union
+
+from radicale import types
+from radicale.auth import none
+
+
+class Auth(none.Auth):
+
+    def get_external_login(self, environ: types.WSGIEnviron) -> Union[
+            Tuple[()], Tuple[str, str]]:
+        return environ.get("HTTP_REMOTE_USER", ""), ""

+ 17 - 0
radicale/tests/test_auth.py

@@ -263,6 +263,23 @@ class TestBaseAuthRequests(BaseTest):
         href_element = prop.find(xmlutils.make_clark("D:href"))
         assert href_element is not None and href_element.text == "/test/"
 
+    def test_http_remote_user(self) -> None:
+        self.configure({"auth": {"type": "http_remote_user"}})
+        _, responses = self.propfind("/", """\
+<?xml version="1.0" encoding="utf-8"?>
+<propfind xmlns="DAV:">
+    <prop>
+        <current-user-principal />
+    </prop>
+</propfind>""", HTTP_REMOTE_USER="test")
+        assert responses is not None
+        response = responses["/"]
+        assert not isinstance(response, int)
+        status, prop = response["D:current-user-principal"]
+        assert status == 200
+        href_element = prop.find(xmlutils.make_clark("D:href"))
+        assert href_element is not None and href_element.text == "/test/"
+
     def test_http_x_remote_user(self) -> None:
         self.configure({"auth": {"type": "http_x_remote_user"}})
         _, responses = self.propfind("/", """\