diff --git a/requirements/base.txt b/requirements/base.txt index 1747c1ecf29a..02531b518eeb 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -160,6 +160,7 @@ greenlet==3.1.1 # via # apache-superset (pyproject.toml) # shillelagh + # sqlalchemy gunicorn==23.0.0 # via apache-superset (pyproject.toml) h11==0.16.0 diff --git a/requirements/development.txt b/requirements/development.txt index 3bc46cb3533f..3de96587944d 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -48,7 +48,7 @@ attrs==25.3.0 # referencing # requests-cache # trio -authlib==1.6.4 +authlib==1.6.5 # via fastmcp babel==2.17.0 # via @@ -176,9 +176,10 @@ cryptography==44.0.3 # paramiko # pyjwt # pyopenssl + # secretstorage cycler==0.12.1 # via matplotlib -cyclopts==3.24.0 +cyclopts==4.2.4 # via fastmcp db-dtypes==1.3.1 # via pandas-gbq @@ -227,7 +228,7 @@ et-xmlfile==2.0.0 # openpyxl exceptiongroup==1.3.0 # via fastmcp -fastmcp==2.13.0.2 +fastmcp==2.13.1 # via apache-superset filelock==3.12.2 # via virtualenv @@ -367,6 +368,7 @@ greenlet==3.1.1 # apache-superset # gevent # shillelagh + # sqlalchemy grpcio==1.71.0 # via # apache-superset @@ -439,6 +441,10 @@ jaraco-context==6.0.1 # via keyring jaraco-functools==4.3.0 # via keyring +jeepney==0.9.0 + # via + # keyring + # secretstorage jinja2==3.1.6 # via # -c requirements/base-constraint.txt @@ -878,6 +884,8 @@ rsa==4.9.1 # google-auth ruff==0.8.0 # via apache-superset +secretstorage==3.4.1 + # via keyring selenium==4.32.0 # via # -c requirements/base-constraint.txt @@ -1022,7 +1030,9 @@ urllib3==2.5.0 # requests-cache # selenium uvicorn==0.37.0 - # via mcp + # via + # fastmcp + # mcp vine==5.1.0 # via # -c requirements/base-constraint.txt diff --git a/superset/security/manager.py b/superset/security/manager.py index fd758d235b1a..f173da512e2f 100644 --- a/superset/security/manager.py +++ b/superset/security/manager.py @@ -2875,7 +2875,30 @@ def register_views(self) -> None: SupersetRegisterUserView ) - super().register_views() + # Apply rate limiting to auth view if enabled + # This needs to be done after the view is added, otherwise the blueprint + # is not initialized. Only apply if blueprint exists. + # We also need to prevent the parent's register_views from trying to + # apply rate limiting again (since auth_view already exists), so we + # temporarily disable AUTH_RATE_LIMITED during the super() call. + if ( + self.is_auth_limited + and getattr(self.auth_view, "blueprint", None) is not None + ): + self.limiter.limit(self.auth_rate_limit, methods=["POST"])( + self.auth_view.blueprint + ) + + # Temporarily disable AUTH_RATE_LIMITED to prevent parent from trying to + # apply rate limiting to a potentially None blueprint + original_auth_rate_limited = current_app.config["AUTH_RATE_LIMITED"] + current_app.config["AUTH_RATE_LIMITED"] = False + + try: + super().register_views() + finally: + # Restore original value even if an exception occurs + current_app.config["AUTH_RATE_LIMITED"] = original_auth_rate_limited for view in list(self.appbuilder.baseviews): if isinstance(view, self.rolemodelview.__class__) and getattr(