add support for http_remote_user

This commit is contained in:
Peter Bieringer
2025-11-15 15:32:13 +01:00
parent a940289798
commit bf8619a41c
6 changed files with 66 additions and 4 deletions

View File

@@ -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,

2
config
View File

@@ -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

View File

@@ -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)

View File

@@ -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",
)

View File

@@ -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", ""), ""

View File

@@ -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("/", """\