You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by ka...@apache.org on 2020/11/20 18:37:58 UTC

[airflow] 01/03: Unify user session lifetime configuration (#11970)

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

kaxilnaik pushed a commit to branch v1-10-test
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 765b6964c1ad539cf46ab5a561eb4df1d9b626f4
Author: MichaƂ Misiewicz <mi...@gmail.com>
AuthorDate: Wed Nov 11 13:28:58 2020 +0100

    Unify user session lifetime configuration (#11970)
    
    * Unify user session lifetime configuration
    
    * align with new linting rules
    
    * exit app when removed args are provided in conf
    
    * add one more test
    
    * extract stopping gunicorn to method
    
    * add docstring to stop_webserver method
    
    * use lazy formatting
    
    * exit webserver when removed options are provided
    
    * align with markdown lint
    
    * Move unify user session lifetime configuration section to master
    
    * add new line
    
    * remove quotes
---
 UPDATING.md                                  | 23 +++++++++++++++++++++++
 airflow/config_templates/config.yml          | 19 ++++++-------------
 airflow/config_templates/default_airflow.cfg |  9 +++------
 airflow/www_rbac/app.py                      | 28 +++++++++++++++++-----------
 tests/www_rbac/test_app.py                   | 25 +++++++++++++++++++++++++
 5 files changed, 74 insertions(+), 30 deletions(-)

diff --git a/UPDATING.md b/UPDATING.md
index e2285df..6dbe8cc 100644
--- a/UPDATING.md
+++ b/UPDATING.md
@@ -78,6 +78,29 @@ support for SSL connection to HDFS).
 
 SSL support still works for WebHDFS hook.
 
+### Unify user session lifetime configuration
+
+In previous version of Airflow user session lifetime could be configured by
+`session_lifetime_days` and `force_log_out_after` options. In practise only `session_lifetime_days`
+had impact on session lifetime, but it was limited to values in day.
+We have removed mentioned options and introduced new `session_lifetime_minutes`
+option which simplify session lifetime configuration.
+
+Before
+
+ ```ini
+[webserver]
+force_log_out_after = 0
+session_lifetime_days = 30
+ ```
+
+After
+
+ ```ini
+[webserver]
+session_lifetime_minutes = 43200
+ ```
+
 ## Airflow 1.10.12
 
 ### Clearing tasks skipped by SkipMixin will skip them
diff --git a/airflow/config_templates/config.yml b/airflow/config_templates/config.yml
index d2621eb..e89df22 100644
--- a/airflow/config_templates/config.yml
+++ b/airflow/config_templates/config.yml
@@ -1001,21 +1001,14 @@
       type: string
       example: ~
       default: "True"
-    - name: force_log_out_after
+    - name: session_lifetime_minutes
       description: |
-        Minutes of non-activity before logged out from UI
-        0 means never get forcibly logged out
-      version_added: 1.10.8
-      type: string
-      example: ~
-      default: "0"
-    - name: session_lifetime_days
-      description: |
-        The UI cookie lifetime in days
-      version_added: 1.10.8
-      type: string
+        The UI cookie lifetime in minutes. User will be logged out from UI after
+        ``session_lifetime_minutes`` of non-activity
+      version_added: 1.10.13
+      type: int
       example: ~
-      default: "30"
+      default: "43200"
 
 - name: email
   description: ~
diff --git a/airflow/config_templates/default_airflow.cfg b/airflow/config_templates/default_airflow.cfg
index 71a5172..ea64d8f 100644
--- a/airflow/config_templates/default_airflow.cfg
+++ b/airflow/config_templates/default_airflow.cfg
@@ -484,12 +484,9 @@ x_frame_enabled = True
 # on webserver startup
 update_fab_perms = True
 
-# Minutes of non-activity before logged out from UI
-# 0 means never get forcibly logged out
-force_log_out_after = 0
-
-# The UI cookie lifetime in days
-session_lifetime_days = 30
+# The UI cookie lifetime in minutes. User will be logged out from UI after
+# ``session_lifetime_minutes`` of non-activity
+session_lifetime_minutes = 43200
 
 [email]
 email_backend = airflow.utils.email.send_email_smtp
diff --git a/airflow/www_rbac/app.py b/airflow/www_rbac/app.py
index 29a364b..0b1a8c7 100644
--- a/airflow/www_rbac/app.py
+++ b/airflow/www_rbac/app.py
@@ -18,6 +18,7 @@
 # under the License.
 #
 import logging
+import sys
 import socket
 from datetime import timedelta
 from typing import Any
@@ -62,8 +63,22 @@ def create_app(config=None, session=None, testing=False, app_name="Airflow"):
         )
     app.secret_key = conf.get('webserver', 'SECRET_KEY')
 
-    session_lifetime_days = conf.getint('webserver', 'SESSION_LIFETIME_DAYS', fallback=30)
-    app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=session_lifetime_days)
+    if conf.has_option('webserver', 'SESSION_LIFETIME_DAYS') or conf.has_option(
+        'webserver', 'FORCE_LOG_OUT_AFTER'
+    ):
+        logging.error(
+            '`SESSION_LIFETIME_DAYS` option from `webserver` section has been '
+            'renamed to `SESSION_LIFETIME_MINUTES`. New option allows to configure '
+            'session lifetime in minutes. FORCE_LOG_OUT_AFTER option has been removed '
+            'from `webserver` section. Please update your configuration.'
+        )
+        # Stop gunicorn server https://github.com/benoitc/gunicorn/blob/20.0.4/gunicorn/arbiter.py#L526
+        # sys.exit(4)
+    else:
+        session_lifetime_minutes = conf.getint('webserver', 'SESSION_LIFETIME_MINUTES', fallback=43200)
+        logging.info('User session lifetime is set to %s minutes.', session_lifetime_minutes)
+
+    app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(minutes=session_lifetime_minutes)
 
     app.config.from_pyfile(settings.WEBSERVER_CONFIG, silent=True)
     app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
@@ -268,15 +283,6 @@ def create_app(config=None, session=None, testing=False, app_name="Airflow"):
         def shutdown_session(exception=None):
             settings.Session.remove()
 
-        @app.before_request
-        def before_request():
-            _force_log_out_after = conf.getint('webserver', 'FORCE_LOG_OUT_AFTER', fallback=0)
-            if _force_log_out_after > 0:
-                flask.session.permanent = True
-                app.permanent_session_lifetime = timedelta(minutes=_force_log_out_after)
-                flask.session.modified = True
-                flask.g.user = flask_login.current_user
-
         @app.after_request
         def apply_caching(response):
             _x_frame_enabled = conf.getboolean('webserver', 'X_FRAME_ENABLED', fallback=True)
diff --git a/tests/www_rbac/test_app.py b/tests/www_rbac/test_app.py
index 0fe8508..a0607cb 100644
--- a/tests/www_rbac/test_app.py
+++ b/tests/www_rbac/test_app.py
@@ -18,11 +18,13 @@
 # under the License.
 import json
 import unittest
+from datetime import timedelta
 
 import pytest
 import six
 from werkzeug.middleware.proxy_fix import ProxyFix
 
+from airflow.configuration import conf
 from airflow.settings import Session
 from airflow.www_rbac import app as application
 from tests.compat import mock
@@ -80,3 +82,26 @@ class TestApp(unittest.TestCase):
         if six.PY2:
             engine_params = json.dumps(engine_params)
         self.assertEqual(app.config['SQLALCHEMY_ENGINE_OPTIONS'], engine_params)
+
+    @conf_vars(
+        {
+            ('webserver', 'session_lifetime_minutes'): '3600',
+        }
+    )
+    @mock.patch("airflow.www_rbac.app.app", None)
+    def test_should_set_permanent_session_timeout(self):
+        app, _ = application.cached_appbuilder(testing=True)
+        self.assertEqual(app.config['PERMANENT_SESSION_LIFETIME'], timedelta(minutes=3600))
+
+    @conf_vars(
+        {
+            ('webserver', 'session_lifetime_days'): '30',
+            ('webserver', 'force_log_out_after'): '30',
+        }
+    )
+    @mock.patch("airflow.www_rbac.app.app", None)
+    def test_should_stop_app_when_removed_options_are_provided(self):
+        with self.assertRaises(SystemExit) as e:
+            conf.remove_option('webserver', 'session_lifetime_minutes')
+            application.cached_appbuilder(testing=True)
+        self.assertEqual(e.exception.code, 4)