Repository: incubator-airflow Updated Branches: refs/heads/v1-10-test c01a12eda -> 7c5157a93
[AIRFLOW-2624] Fix webserver login as anonymous (cherry picked from commit 2fd9328b412841429acc288b1441c8351ee15e98) Signed-off-by: Bolke de Bruin <bo...@xs4all.nl> Project: http://git-wip-us.apache.org/repos/asf/incubator-airflow/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-airflow/commit/7c5157a9 Tree: http://git-wip-us.apache.org/repos/asf/incubator-airflow/tree/7c5157a9 Diff: http://git-wip-us.apache.org/repos/asf/incubator-airflow/diff/7c5157a9 Branch: refs/heads/v1-10-test Commit: 7c5157a9366dfb118bccb0c900313ea46e5eb80f Parents: c01a12e Author: Kevin Yang <kevin.y...@airbnb.com> Authored: Thu Jun 14 18:20:46 2018 -0700 Committer: Bolke de Bruin <bo...@xs4all.nl> Committed: Wed Jun 27 22:17:08 2018 +0200 ---------------------------------------------------------------------- airflow/www/utils.py | 6 ++-- tests/www/test_utils.py | 85 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-airflow/blob/7c5157a9/airflow/www/utils.py ---------------------------------------------------------------------- diff --git a/airflow/www/utils.py b/airflow/www/utils.py index 7d9c8a0..44fa5c4 100644 --- a/airflow/www/utils.py +++ b/airflow/www/utils.py @@ -246,8 +246,8 @@ def action_logging(f): """ @functools.wraps(f) def wrapper(*args, **kwargs): - # Only AnonymousUserMixin() does not have user attribute - if current_user and hasattr(current_user, 'user'): + # AnonymousUserMixin() has user attribute but its value is None. + if current_user and hasattr(current_user, 'user') and current_user.user: user = current_user.user.username else: user = 'anonymous' @@ -286,7 +286,7 @@ def notify_owner(f): dag = dagbag.get_dag(dag_id) task = dag.get_task(task_id) - if current_user and hasattr(current_user, 'username'): + if current_user and hasattr(current_user, 'user') and current_user.user: user = current_user.username else: user = 'anonymous' http://git-wip-us.apache.org/repos/asf/incubator-airflow/blob/7c5157a9/tests/www/test_utils.py ---------------------------------------------------------------------- diff --git a/tests/www/test_utils.py b/tests/www/test_utils.py index d69041a..9d788e8 100644 --- a/tests/www/test_utils.py +++ b/tests/www/test_utils.py @@ -7,9 +7,9 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -17,9 +17,12 @@ # specific language governing permissions and limitations # under the License. +import mock import unittest from xml.dom import minidom +from airflow.www import app as application + from airflow.www import utils @@ -109,6 +112,84 @@ class UtilsTest(unittest.TestCase): self.assertEqual('page=3&search=bash_&showPaused=False', utils.get_params(showPaused=False, page=3, search='bash_')) + # flask_login is loaded by calling flask_login._get_user. + @mock.patch("flask_login._get_user") + @mock.patch("airflow.settings.Session") + def test_action_logging_with_login_user(self, mocked_session, mocked_get_user): + fake_username = 'someone' + mocked_current_user = mock.MagicMock() + mocked_get_user.return_value = mocked_current_user + mocked_current_user.user.username = fake_username + mocked_session_instance = mock.MagicMock() + mocked_session.return_value = mocked_session_instance + + app = application.create_app(testing=True) + # Patching here to avoid errors in applicant.create_app + with mock.patch("airflow.models.Log") as mocked_log: + with app.test_request_context(): + @utils.action_logging + def some_func(): + pass + + some_func() + mocked_log.assert_called_once() + (args, kwargs) = mocked_log.call_args_list[0] + self.assertEqual('some_func', kwargs['event']) + self.assertEqual(fake_username, kwargs['owner']) + mocked_session_instance.add.assert_called_once() + + @mock.patch("flask_login._get_user") + @mock.patch("airflow.settings.Session") + def test_action_logging_with_invalid_user(self, mocked_session, mocked_get_user): + anonymous_username = 'anonymous' + + # When the user returned by flask login_manager._load_user + # is invalid. + mocked_current_user = mock.MagicMock() + mocked_get_user.return_value = mocked_current_user + mocked_current_user.user = None + mocked_session_instance = mock.MagicMock() + mocked_session.return_value = mocked_session_instance + + app = application.create_app(testing=True) + # Patching here to avoid errors in applicant.create_app + with mock.patch("airflow.models.Log") as mocked_log: + with app.test_request_context(): + @utils.action_logging + def some_func(): + pass + + some_func() + mocked_log.assert_called_once() + (args, kwargs) = mocked_log.call_args_list[0] + self.assertEqual('some_func', kwargs['event']) + self.assertEqual(anonymous_username, kwargs['owner']) + mocked_session_instance.add.assert_called_once() + + # flask_login.current_user would be AnonymousUserMixin + # when there's no user_id in the flask session. + @mock.patch("airflow.settings.Session") + def test_action_logging_with_anonymous_user(self, mocked_session): + anonymous_username = 'anonymous' + + mocked_session_instance = mock.MagicMock() + mocked_session.return_value = mocked_session_instance + + app = application.create_app(testing=True) + # Patching here to avoid errors in applicant.create_app + with mock.patch("airflow.models.Log") as mocked_log: + with app.test_request_context(): + @utils.action_logging + def some_func(): + pass + + some_func() + mocked_log.assert_called_once() + (args, kwargs) = mocked_log.call_args_list[0] + self.assertEqual('some_func', kwargs['event']) + self.assertEqual(anonymous_username, kwargs['owner']) + mocked_session_instance.add.assert_called_once() + if __name__ == '__main__': unittest.main()