Skip to content

Commit 5466755

Browse files
authored
Add ability to configure PERMANENT_SESSION_LIFETIME in fab for session logout. (apache#48827)
1 parent a5fd5bb commit 5466755

File tree

10 files changed

+84
-52
lines changed

10 files changed

+84
-52
lines changed

airflow-core/newsfragments/41550.significant.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Removed deprecated ``session_lifetime_days`` and ``force_log_out_after`` configuration parameters from ``webserver`` section. Please use ``session_lifetime_minutes``.
1+
Removed deprecated ``session_lifetime_days`` and ``force_log_out_after`` configuration parameters from ``webserver`` section. Please use ``session_lifetime_minutes`` from ``fab`` section.
22

33
Removed deprecated ``policy`` parameter from ``airflow_local_settings``. Please use ``task_policy``.
44

@@ -17,6 +17,6 @@ Removed deprecated ``policy`` parameter from ``airflow_local_settings``. Please
1717

1818
* ``airflow config lint``
1919

20-
* [x] ``webserver.session_lifetime_days`` → ``webserver.session_lifetime_minutes``
21-
* [x] ``webserver.force_log_out_after`` → ``webserver.session_lifetime_minutes``
20+
* [x] ``webserver.session_lifetime_days`` → ``fab.session_lifetime_minutes``
21+
* [x] ``webserver.force_log_out_after`` → ``fab.session_lifetime_minutes``
2222
* [x] ``policy`` → ``task_policy``

airflow-core/src/airflow/cli/commands/config_command.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,11 +329,15 @@ def message(self) -> str | None:
329329
),
330330
ConfigChange(
331331
config=ConfigParameter("webserver", "session_lifetime_days"),
332-
renamed_to=ConfigParameter("webserver", "session_lifetime_minutes"),
332+
renamed_to=ConfigParameter("fab", "session_lifetime_minutes"),
333333
),
334334
ConfigChange(
335335
config=ConfigParameter("webserver", "force_log_out_after"),
336-
renamed_to=ConfigParameter("webserver", "session_lifetime_minutes"),
336+
renamed_to=ConfigParameter("fab", "session_lifetime_minutes"),
337+
),
338+
ConfigChange(
339+
config=ConfigParameter("webserver", "session_lifetime_minutes"),
340+
renamed_to=ConfigParameter("fab", "session_lifetime_minutes"),
337341
),
338342
ConfigChange(
339343
config=ConfigParameter("webserver", "web_server_host"),

airflow-core/src/airflow/config_templates/config.yml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1862,14 +1862,6 @@ webserver:
18621862
type: boolean
18631863
example: ~
18641864
default: "True"
1865-
session_lifetime_minutes:
1866-
description: |
1867-
The UI cookie lifetime in minutes. User will be logged out from UI after
1868-
``[webserver] session_lifetime_minutes`` of non-activity
1869-
version_added: 1.10.13
1870-
type: integer
1871-
example: ~
1872-
default: "43200"
18731865
instance_name:
18741866
description: |
18751867
Sets a custom page title for the DAGs overview page and site title for all pages

airflow-core/src/airflow/settings.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -585,19 +585,6 @@ def prepare_syspath_for_dags_folder():
585585
sys.path.append(DAGS_FOLDER)
586586

587587

588-
def get_session_lifetime_config():
589-
"""Get session timeout configs and handle outdated configs gracefully."""
590-
session_lifetime_minutes = conf.get("webserver", "session_lifetime_minutes", fallback=None)
591-
minutes_per_day = 24 * 60
592-
if not session_lifetime_minutes:
593-
session_lifetime_days = 30
594-
session_lifetime_minutes = minutes_per_day * session_lifetime_days
595-
596-
log.debug("User session lifetime is set to %s minutes.", session_lifetime_minutes)
597-
598-
return int(session_lifetime_minutes)
599-
600-
601588
def import_local_settings():
602589
"""Import airflow_local_settings.py files to allow overriding any configs in settings.py file."""
603590
try:

airflow-core/tests/unit/core/test_settings.py

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828

2929
from airflow.exceptions import AirflowClusterPolicyViolation, AirflowConfigException
3030

31-
from tests_common.test_utils.config import conf_vars
32-
3331
SETTINGS_FILE_POLICY = """
3432
def test_policy(task_instance):
3533
task_instance.run_as_user = "myself"
@@ -209,30 +207,6 @@ def test_custom_policy(self):
209207
settings.task_must_have_owners(task_instance)
210208

211209

212-
class TestUpdatedConfigNames:
213-
@conf_vars({("webserver", "session_lifetime_minutes"): "43200"})
214-
def test_config_val_is_default(self):
215-
from airflow import settings
216-
217-
session_lifetime_config = settings.get_session_lifetime_config()
218-
assert session_lifetime_config == 43200
219-
220-
@conf_vars({("webserver", "session_lifetime_minutes"): "43201"})
221-
def test_config_val_is_not_default(self):
222-
from airflow import settings
223-
224-
session_lifetime_config = settings.get_session_lifetime_config()
225-
assert session_lifetime_config == 43201
226-
227-
@conf_vars({("webserver", "session_lifetime_days"): ""})
228-
def test_uses_updated_session_timeout_config_by_default(self):
229-
from airflow import settings
230-
231-
session_lifetime_config = settings.get_session_lifetime_config()
232-
default_timeout_minutes = 30 * 24 * 60
233-
assert session_lifetime_config == default_timeout_minutes
234-
235-
236210
_local_db_path_error = pytest.raises(AirflowConfigException, match=r"Cannot use relative path:")
237211

238212

providers/fab/provider.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ config:
8383
type: string
8484
example: ~
8585
default: "airflow.providers.fab.auth_manager.api.auth.backend.session"
86+
session_lifetime_minutes:
87+
description: |
88+
The UI cookie lifetime in minutes. User will be logged out from UI after
89+
``[fab] session_lifetime_minutes`` of non-activity
90+
version_added: 2.0.0
91+
type: integer
92+
example: ~
93+
default: "43200"
8694

8795
auth-managers:
8896
- airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager

providers/fab/src/airflow/providers/fab/get_provider_info.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ def get_provider_info():
5858
"example": None,
5959
"default": "airflow.providers.fab.auth_manager.api.auth.backend.session",
6060
},
61+
"session_lifetime_minutes": {
62+
"description": "The UI cookie lifetime in minutes. User will be logged out from UI after\n``[fab] session_lifetime_minutes`` of non-activity\n",
63+
"version_added": "2.0.0",
64+
"type": "integer",
65+
"example": None,
66+
"default": "43200",
67+
},
6168
},
6269
}
6370
},

providers/fab/src/airflow/providers/fab/www/app.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
# under the License.
1818
from __future__ import annotations
1919

20+
from datetime import timedelta
2021
from os.path import isabs
2122

2223
from flask import Flask
@@ -40,6 +41,7 @@
4041
init_error_handlers,
4142
init_plugins,
4243
)
44+
from airflow.providers.fab.www.utils import get_session_lifetime_config
4345

4446
app: Flask | None = None
4547

@@ -56,6 +58,8 @@ def create_app(enable_plugins: bool):
5658
flask_app.secret_key = conf.get("webserver", "SECRET_KEY")
5759
flask_app.config["SQLALCHEMY_DATABASE_URI"] = conf.get("database", "SQL_ALCHEMY_CONN")
5860
flask_app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
61+
flask_app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(minutes=get_session_lifetime_config())
62+
5963
webserver_config = conf.get_mandatory_value("webserver", "config_file")
6064
# Enable customizations in webserver_config.py to be applied via Flask.current_app.
6165
with flask_app.app_context():

providers/fab/src/airflow/providers/fab/www/utils.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
# under the License.
1818
from __future__ import annotations
1919

20+
import logging
2021
from typing import TYPE_CHECKING, Any
2122

2223
from flask_appbuilder.models.filters import BaseFilter
@@ -28,6 +29,7 @@
2829
from sqlalchemy.ext.associationproxy import AssociationProxy
2930

3031
from airflow.api_fastapi.app import get_auth_manager
32+
from airflow.configuration import conf
3133
from airflow.providers.fab.www.security.permissions import (
3234
ACTION_CAN_ACCESS_MENU,
3335
ACTION_CAN_CREATE,
@@ -51,6 +53,20 @@
5153
"DELETE": ACTION_CAN_DELETE,
5254
"MENU": ACTION_CAN_ACCESS_MENU,
5355
}
56+
log = logging.getLogger(__name__)
57+
58+
59+
def get_session_lifetime_config():
60+
"""Get session timeout configs and handle outdated configs gracefully."""
61+
session_lifetime_minutes = conf.get("fab", "session_lifetime_minutes", fallback=None)
62+
minutes_per_day = 24 * 60
63+
if not session_lifetime_minutes:
64+
session_lifetime_days = 30
65+
session_lifetime_minutes = minutes_per_day * session_lifetime_days
66+
67+
log.debug("User session lifetime is set to %s minutes.", session_lifetime_minutes)
68+
69+
return int(session_lifetime_minutes)
5470

5571

5672
def get_fab_auth_manager() -> FabAuthManager:
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
from __future__ import annotations
19+
20+
from airflow.providers.fab.www.utils import get_session_lifetime_config
21+
22+
from tests_common.test_utils.config import conf_vars
23+
24+
25+
class TestUpdatedConfigNames:
26+
@conf_vars({("fab", "session_lifetime_minutes"): "43200"})
27+
def test_config_val_is_default(self):
28+
session_lifetime_config = get_session_lifetime_config()
29+
assert session_lifetime_config == 43200
30+
31+
@conf_vars({("fab", "session_lifetime_minutes"): "43201"})
32+
def test_config_val_is_not_default(self):
33+
session_lifetime_config = get_session_lifetime_config()
34+
assert session_lifetime_config == 43201
35+
36+
@conf_vars({("fab", "session_lifetime_days"): ""})
37+
def test_uses_updated_session_timeout_config_by_default(self):
38+
session_lifetime_config = get_session_lifetime_config()
39+
default_timeout_minutes = 30 * 24 * 60
40+
assert session_lifetime_config == default_timeout_minutes

0 commit comments

Comments
 (0)