You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by je...@apache.org on 2022/09/26 15:09:20 UTC

[airflow] 11/13: Check user is active (#26635)

This is an automated email from the ASF dual-hosted git repository.

jedcunningham pushed a commit to branch v2-4-test
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 85895b567f5f70ffc497d84570223c5fb80f7de4
Author: Jed Cunningham <66...@users.noreply.github.com>
AuthorDate: Fri Sep 23 13:28:33 2022 -0700

    Check user is active (#26635)
    
    (cherry picked from commit 59707cdf7eacb698ca375b5220af30a39ca1018c)
---
 airflow/www/app.py                      |  7 ++++++-
 airflow/www/extensions/init_security.py | 11 +++++++++++
 tests/test_utils/decorators.py          |  1 +
 tests/www/views/conftest.py             |  1 +
 tests/www/views/test_session.py         | 14 ++++++++++++++
 tests/www/views/test_views_base.py      | 13 +++++++++++--
 6 files changed, 44 insertions(+), 3 deletions(-)

diff --git a/airflow/www/app.py b/airflow/www/app.py
index b67314c99a..d0c38b2936 100644
--- a/airflow/www/app.py
+++ b/airflow/www/app.py
@@ -39,7 +39,11 @@ from airflow.www.extensions.init_dagbag import init_dagbag
 from airflow.www.extensions.init_jinja_globals import init_jinja_globals
 from airflow.www.extensions.init_manifest_files import configure_manifest_files
 from airflow.www.extensions.init_robots import init_robots
-from airflow.www.extensions.init_security import init_api_experimental_auth, init_xframe_protection
+from airflow.www.extensions.init_security import (
+    init_api_experimental_auth,
+    init_check_user_active,
+    init_xframe_protection,
+)
 from airflow.www.extensions.init_session import init_airflow_session_interface
 from airflow.www.extensions.init_views import (
     init_api_connexion,
@@ -152,6 +156,7 @@ def create_app(config=None, testing=False):
         init_jinja_globals(flask_app)
         init_xframe_protection(flask_app)
         init_airflow_session_interface(flask_app)
+        init_check_user_active(flask_app)
     return flask_app
 
 
diff --git a/airflow/www/extensions/init_security.py b/airflow/www/extensions/init_security.py
index 1d96e351df..b967b74084 100644
--- a/airflow/www/extensions/init_security.py
+++ b/airflow/www/extensions/init_security.py
@@ -19,6 +19,9 @@ from __future__ import annotations
 import logging
 from importlib import import_module
 
+from flask import g, redirect, url_for
+from flask_login import logout_user
+
 from airflow.configuration import conf
 from airflow.exceptions import AirflowConfigException, AirflowException
 
@@ -60,3 +63,11 @@ def init_api_experimental_auth(app):
         except ImportError as err:
             log.critical("Cannot import %s for API authentication due to: %s", backend, err)
             raise AirflowException(err)
+
+
+def init_check_user_active(app):
+    @app.before_request
+    def check_user_active():
+        if g.user is not None and not g.user.is_anonymous and not g.user.is_active:
+            logout_user()
+            return redirect(url_for(app.appbuilder.sm.auth_view.endpoint + ".login"))
diff --git a/tests/test_utils/decorators.py b/tests/test_utils/decorators.py
index bdb8d67807..d0b71b502c 100644
--- a/tests/test_utils/decorators.py
+++ b/tests/test_utils/decorators.py
@@ -45,6 +45,7 @@ def dont_initialize_flask_app_submodules(_func=None, *, skip_all_except=None):
             "init_xframe_protection",
             "init_airflow_session_interface",
             "init_appbuilder",
+            "init_check_user_active",
         ]
 
         @functools.wraps(f)
diff --git a/tests/www/views/conftest.py b/tests/www/views/conftest.py
index 02c857180f..ad562385bc 100644
--- a/tests/www/views/conftest.py
+++ b/tests/www/views/conftest.py
@@ -58,6 +58,7 @@ def app(examples_dag_bag):
             "init_jinja_globals",
             "init_plugins",
             "init_airflow_session_interface",
+            "init_check_user_active",
         ]
     )
     def factory():
diff --git a/tests/www/views/test_session.py b/tests/www/views/test_session.py
index 090bc503a8..3802399264 100644
--- a/tests/www/views/test_session.py
+++ b/tests/www/views/test_session.py
@@ -88,3 +88,17 @@ def test_session_id_rotates(app, user_client):
     new_session_cookie = get_session_cookie(user_client)
     assert new_session_cookie is not None
     assert old_session_cookie.value != new_session_cookie.value
+
+
+def test_check_active_user(app, user_client):
+    user = app.appbuilder.sm.find_user(username="test_user")
+    user.active = False
+    resp = user_client.get("/home")
+    assert resp.status_code == 302
+    assert "/login" in resp.headers.get("Location")
+
+    # And they were logged out
+    user.active = True
+    resp = user_client.get("/home")
+    assert resp.status_code == 302
+    assert "/login" in resp.headers.get("Location")
diff --git a/tests/www/views/test_views_base.py b/tests/www/views/test_views_base.py
index d0acc4df27..9c9c4f0aba 100644
--- a/tests/www/views/test_views_base.py
+++ b/tests/www/views/test_views_base.py
@@ -30,9 +30,18 @@ from tests.test_utils.config import conf_vars
 from tests.test_utils.www import check_content_in_response, check_content_not_in_response
 
 
-def test_index(admin_client):
+def test_index_redirect(admin_client):
+    resp = admin_client.get('/')
+    assert resp.status_code == 302
+    assert '/home' in resp.headers.get("Location")
+
+    resp = admin_client.get('/', follow_redirects=True)
+    check_content_in_response('DAGs', resp)
+
+
+def test_homepage_query_count(admin_client):
     with assert_queries_count(16):
-        resp = admin_client.get('/', follow_redirects=True)
+        resp = admin_client.get('/home')
     check_content_in_response('DAGs', resp)