From a99e0e444ff35f57e89df40030c5b035832497ca Mon Sep 17 00:00:00 2001 From: Maximilian Moser <maximilian.moser@tuwien.ac.at> Date: Wed, 5 Feb 2025 00:15:59 +0100 Subject: [PATCH 1/3] Move Flask config override from ext to startup * apparently it was a leftover `before_first_request` handler * reworked it as `finalize_app` entrypoint * also, added a check if the app config is already a TUW config to avoid unnecessary nesting --- invenio_config_tuw/ext.py | 21 --------------------- invenio_config_tuw/startup.py | 33 +++++++++++++++++++++++++++++++++ pyproject.toml | 2 ++ 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/invenio_config_tuw/ext.py b/invenio_config_tuw/ext.py index d21bbc9..341e041 100644 --- a/invenio_config_tuw/ext.py +++ b/invenio_config_tuw/ext.py @@ -10,7 +10,6 @@ from typing import List from flask import current_app -from flask.config import Config from flask_minify import Minify from flask_security.signals import user_registered from invenio_base.utils import obj_or_import_string @@ -19,26 +18,6 @@ from . import config from .auth.utils import auto_trust_user -class TUWConfig(Config): - """Override for the Flask config that evaluates the SITE_{API,UI}_URL proxies.""" - - @classmethod - def from_flask_config(cls, config): - """Create a clone of the given config.""" - return cls(config.root_path, config) - - def __getitem__(self, key): - """Return config[key], or str(config[key]) if key is 'SITE_{UI,API}_URL'.""" - value = super().__getitem__(key) - - # give special treatment to the URL configuration items: - # enforce their evaluation as strings - if key in ("SITE_UI_URL", "SITE_API_URL"): - value = str(value) - - return value - - @user_registered.connect def auto_trust_new_user(sender, user, **kwargs): """Execute `auto_trust_user()` on newly created users. diff --git a/invenio_config_tuw/startup.py b/invenio_config_tuw/startup.py index 4fa84a4..73a1b71 100644 --- a/invenio_config_tuw/startup.py +++ b/invenio_config_tuw/startup.py @@ -17,6 +17,7 @@ from logging import ERROR from logging.handlers import SMTPHandler import importlib_metadata +from flask.config import Config from invenio_rdm_records.services.search_params import MyDraftsParam from invenio_requests.proxies import current_request_type_registry @@ -24,6 +25,29 @@ from .curations import TUWCurationRequest from .logs import DetailedFormatter +class TUWConfig(Config): + """Override for the Flask config that evaluates the SITE_{API,UI}_URL proxies.""" + + @classmethod + def from_flask_config(cls, config): + """Create a clone of the given config.""" + if isinstance(config, TUWConfig): + return config + + return cls(config.root_path, config) + + def __getitem__(self, key): + """Return config[key], or str(config[key]) if key is 'SITE_{UI,API}_URL'.""" + value = super().__getitem__(key) + + # give special treatment to the URL configuration items: + # enforce their evaluation as strings + if key in ("SITE_UI_URL", "SITE_API_URL"): + value = str(value) + + return value + + def register_smtp_error_handler(app): """Register email error handler to the application.""" handler_name = "invenio-config-tuw-smtp-error-handler" @@ -109,6 +133,15 @@ def customize_curation_request_type(app): current_request_type_registry.register_type(TUWCurationRequest(), force=True) +def override_flask_config(app): + """Replace the app's config with our own override. + + This evaluates the ``LocalProxy`` objects used for ``SITE_{API,UI}_URL`` by + casting them into strings (which is their expected type). + """ + app.config = TUWConfig.from_flask_config(app.config) + + def patch_flask_create_url_adapter(app): """Patch Flask's {host,subdomain}_matching with 3.1 behavior. diff --git a/pyproject.toml b/pyproject.toml index e3e12da..dc5cb12 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,12 +81,14 @@ invenio_config_tuw_mail_handler = "invenio_config_tuw.startup:register_smtp_erro invenio_config_tuw_search_drafts = "invenio_config_tuw.startup:override_search_drafts_options" invenio_config_tuw_curation_settings = "invenio_config_tuw.startup:register_menu_entries" invenio_config_tuw_curation_request = "invenio_config_tuw.startup:customize_curation_request_type" +invenio_config_tuw_flask_config = "invenio_config_tuw.startup:override_flask_config" invenio_config_tuw_patch_flask = "invenio_config_tuw.startup:patch_flask_create_url_adapter" [project.entry-points."invenio_base.api_finalize_app"] invenio_config_tuw_mail_handler = "invenio_config_tuw.startup:register_smtp_error_handler" invenio_config_tuw_search_drafts = "invenio_config_tuw.startup:override_search_drafts_options" invenio_config_tuw_curation_request = "invenio_config_tuw.startup:customize_curation_request_type" +invenio_config_tuw_flask_config = "invenio_config_tuw.startup:override_flask_config" invenio_config_tuw_patch_flask = "invenio_config_tuw.startup:patch_flask_create_url_adapter" [project.entry-points."invenio_celery.tasks"] -- GitLab From e2cd5b79345197fc19a65067be256a8563d490e8 Mon Sep 17 00:00:00 2001 From: Maximilian Moser <maximilian.moser@tuwien.ac.at> Date: Wed, 5 Feb 2025 00:18:12 +0100 Subject: [PATCH 2/3] Remove `SERVER_NAME` config for the duration of requests * because otherwise, `url_for()` will prefer that value over the HTTP Host header value * this causes issues for us with our multi-domain setup, in conjunction with the OIDC redirects - given that `SERVER_NAME` can only be a scalar value, trying to login on a secondary domain will redirect to the primary one and typically fail (because sessions are different, etc.) * also make sure that the `APP_ALLOWED_HOSTS` config contains the `SERVER_NAME` if set --- invenio_config_tuw/ext.py | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/invenio_config_tuw/ext.py b/invenio_config_tuw/ext.py index 341e041..1f8d636 100644 --- a/invenio_config_tuw/ext.py +++ b/invenio_config_tuw/ext.py @@ -42,14 +42,40 @@ class InvenioConfigTUW(object): """Flask application initialization.""" self.init_config(app) self.init_minify(app) + self.handle_server_name(app) app.extensions["invenio-config-tuw"] = self - @app.before_first_request - def hack_app_config(): - # replace the app's config with our own override that evaluates the - # LocalProxy objects used for SITE_{API,UI}_URL by casting them into strings - # (which is their expected type) - app.config = TUWConfig.from_flask_config(app.config) + def handle_server_name(self, app): + """Pop the `SERVER_NAME` configuration item between requests. + + This can be useful in multi-domain setups where for some reason, absolute + URLs with the currently requested hostname need to be generated inside an + active request context (e.g. OIDC redirect URIs). + It seems like if `SERVER_NAME` is set, it will take precedence over + HTTP `Host` when calling `url_for()`. + """ + self.server_name = app.config.get("SERVER_NAME", None) + + # since allowing the client to set arbitrary vlaues of the HTTP Host header + # field can lead to arbitrary redirects, it's important to keep track of + # allowed values + allowed_hosts = app.config.get("APP_ALLOWED_HOSTS", []) + if self.server_name and self.server_name not in allowed_hosts: + allowed_hosts.append(self.server_name) + + app.config["APP_ALLOWED_HOSTS"] = app.config["ALLOWED_HOSTS"] = allowed_hosts + + @app.before_request + def pop_server_name(): + """Unset `SERVER_NAME` to prefer the HOST HTTP header value.""" + self.server_name = app.config.get("SERVER_NAME", None) + app.config["SERVER_NAME"] = None + + @app.after_request + def restore_server_name(response): + """Restore `SERVER_NAME` enable creating URLs outside of requests.""" + app.config.setdefault("SERVER_NAME", self.server_name) + return response def init_config(self, app): """Initialize configuration.""" -- GitLab From c5ac5a72221b372336b0c525d81433641a6210bc Mon Sep 17 00:00:00 2001 From: Maximilian Moser <maximilian.moser@tuwien.ac.at> Date: Wed, 5 Feb 2025 00:31:05 +0100 Subject: [PATCH 3/3] Bump version to v2025.1.2 --- CHANGES.rst | 6 ++++++ invenio_config_tuw/__init__.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index dd8daae..1dcd0c3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,6 +9,12 @@ Changes ======= +Version 2025.1.2 (released 2025-02-05) + +- Rework the Flask config class override as a `finalize_app` handler +- Temporarily unset `SERVER_NAME` during requests, to avoid forcing it for `url_for()` + + Version 2025.1.1 (released 2025-02-04) - Override `Flask.create_url_adapter()` to match v3.1 regarding `SERVER_NAME` diff --git a/invenio_config_tuw/__init__.py b/invenio_config_tuw/__init__.py index 0e0113e..75f9ca0 100644 --- a/invenio_config_tuw/__init__.py +++ b/invenio_config_tuw/__init__.py @@ -9,6 +9,6 @@ from .ext import InvenioConfigTUW -__version__ = "2025.1.1" +__version__ = "2025.1.2" __all__ = ("__version__", "InvenioConfigTUW") -- GitLab