From b3adf67b4962a2d694cf55bd80e9148d5c535ead Mon Sep 17 00:00:00 2001 From: Igor Serebryany Date: Tue, 26 Sep 2023 14:52:38 -0700 Subject: [PATCH] allow creating users from reverse proxy headers allowing login via reverse proxy auth is convenient, but it's not convenient to have to create the users in advance. this PR allows users to be optionally created if they don't already exist. we provide this as an option in the UI --- cps/admin.py | 2 ++ cps/config_sql.py | 4 +++- cps/templates/admin.html | 8 +++++++ cps/templates/config_edit.html | 8 +++++++ cps/usermanagement.py | 44 ++++++++++++++++++++++++++++++++++ messages.pot | 8 +++++++ 6 files changed, 73 insertions(+), 1 deletion(-) diff --git a/cps/admin.py b/cps/admin.py index 93c1a3a9..ac1f2329 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -1793,6 +1793,8 @@ def _configuration_update_helper(): # Reverse proxy login configuration _config_checkbox(to_save, "config_allow_reverse_proxy_header_login") _config_string(to_save, "config_reverse_proxy_login_header_name") + _config_checkbox(to_save, "config_reverse_proxy_create_users") + _config_string(to_save, "config_reverse_proxy_email_header_name") # OAuth configuration if config.config_login_type == constants.LOGIN_OAUTH: diff --git a/cps/config_sql.py b/cps/config_sql.py index 21644ccd..13045cdc 100644 --- a/cps/config_sql.py +++ b/cps/config_sql.py @@ -79,7 +79,7 @@ class _Settings(_Base): config_random_books = Column(Integer, default=4) config_authors_max = Column(Integer, default=0) config_read_column = Column(Integer, default=0) - config_title_regex = Column(String, default=r'^(A|The|An|Der|Die|Das|Den|Ein|Eine|Einen|Dem|Des|Einem|Eines|Le|La|Les|L\'|Un|Une)\s+') + config_title_regex = Column(String, default=r'^(A|The|An|Der|Die|Das|Den|Ein|Eine|Einen|Dem|Des|Einem|Eines|Le|La|Les|L\'|Un|Une)\s+') config_theme = Column(Integer, default=0) config_log_level = Column(SmallInteger, default=logger.DEFAULT_LOG_LEVEL) @@ -147,6 +147,8 @@ class _Settings(_Base): config_reverse_proxy_login_header_name = Column(String) config_allow_reverse_proxy_header_login = Column(Boolean, default=False) + config_reverse_proxy_create_users = Column(Boolean, default=False) + config_reverse_proxy_email_header_name = Column(String) schedule_start_time = Column(Integer, default=4) schedule_duration = Column(Integer, default=10) diff --git a/cps/templates/admin.html b/cps/templates/admin.html index ac124fe8..4158e648 100644 --- a/cps/templates/admin.html +++ b/cps/templates/admin.html @@ -154,6 +154,14 @@
{{_('Reverse Proxy Header Name')}}
{{ config.config_reverse_proxy_login_header_name }}
+
+
{{_('Create Reverse Proxy Users')}}
+
{{ display_bool_setting(config.config_reverse_proxy_create_users) }}
+
+
+
{{_('Reverse Proxy Email Header Name')}}
+
{{ config.config_reverse_proxy_email_header_name }}
+
{% endif %} {{_('Edit Calibre Database Configuration')}} diff --git a/cps/templates/config_edit.html b/cps/templates/config_edit.html index e259ac2f..5a3cbb22 100644 --- a/cps/templates/config_edit.html +++ b/cps/templates/config_edit.html @@ -173,6 +173,14 @@ +
+ + +
+
+ + +
{% if not config.config_is_initial %} {% if feature_support['ldap'] or feature_support['oauth'] %} diff --git a/cps/usermanagement.py b/cps/usermanagement.py index d8f64012..e8edb93a 100644 --- a/cps/usermanagement.py +++ b/cps/usermanagement.py @@ -25,6 +25,8 @@ from flask import request, Response from . import lm, ub, config, constants, services, logger, limiter +from .helper import generate_random_password, generate_password_hash, check_email + log = logger.create() def login_required_if_no_ano(func): @@ -103,9 +105,51 @@ def load_user_from_reverse_proxy_header(req): rp_header_username = req.headers.get(rp_header_name) if rp_header_username: user = _fetch_user_by_name(rp_header_username) + if not user and config.config_reverse_proxy_create_users: + create_user_from_reverse_proxy_header(req) + user = _fetch_user_by_name(rp_header_username) + if user: [limiter.limiter.storage.clear(k.key) for k in limiter.current_limits] login_user(user) return user return None + +def create_user_from_reverse_proxy_header(req): + rp_header_name = config.config_reverse_proxy_login_header_name + username = req.headers.get(rp_header_name) + + # does the user have an email address in the headers? + rp_email_header_name = config.config_reverse_proxy_email_header_name + if rp_email_header_name: + try: + email = check_email(req.headers.get(rp_email_header_name)) + except Exception: + log.debug('No email address found in Reverse Proxy headers') + email = username + '@localhost' + + # generate a random password + password = generate_random_password(config.config_password_min_length) + pwhash = generate_password_hash(password) + + user = ub.User() + user.name = username + user.password = pwhash + user.email = email + user.default_language = config.config_default_language + user.locale = config.config_default_locale + user.role = config.config_default_role + user.sidebar_view = config.config_default_show + user.allowed_tags = config.config_allowed_tags + user.denied_tags = config.config_denied_tags + user.allowed_column_value = config.config_allowed_column_value + user.denied_column_value = config.config_denied_column_value + + # save the user + ub.session.add(user) + try: + ub.session.commit() + except Exception as ex: + log.warning("Failed to create Reverse Proxy user: %s - %s", username, ex) + ub.session.rollback() diff --git a/messages.pot b/messages.pot index b5cad055..8243fe9d 100644 --- a/messages.pot +++ b/messages.pot @@ -1639,6 +1639,14 @@ msgstr "" msgid "Reverse Proxy Header Name" msgstr "" +#: cps/templates/admin.html:158 cps/templates/config_edit.html:178 +msgid "Create Reverse Proxy Users" +msgstr "" + +#: cps/templates/admin.html:162 cps/templates/config_edit.html:181 +msgid "Reverse Proxy Email Header Name" +msgstr "" + #: cps/templates/admin.html:159 msgid "Edit Calibre Database Configuration" msgstr ""