apache-airflow-providers-fab 2.4.1__py3-none-any.whl → 2.4.2rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. airflow/providers/fab/__init__.py +1 -1
  2. airflow/providers/fab/auth_manager/fab_auth_manager.py +45 -40
  3. airflow/providers/fab/auth_manager/models/__init__.py +2 -2
  4. airflow/providers/fab/auth_manager/security_manager/override.py +71 -45
  5. airflow/providers/fab/get_provider_info.py +14 -0
  6. airflow/providers/fab/migrations/versions/0001_1_4_0_create_ab_tables_if_missing.py +220 -0
  7. airflow/providers/fab/www/package-lock.json +117 -106
  8. airflow/providers/fab/www/package.json +8 -8
  9. airflow/providers/fab/www/security/permissions.py +7 -8
  10. airflow/providers/fab/www/static/dist/{743.27a753a06671118f1c5c.js → 743.fc7a7c6ef9d09365976e.js} +1 -1
  11. airflow/providers/fab/www/static/dist/{main.810554d06c3e30f2484e.js → main.3cf3be1a0c5439bb640d.js} +1 -1
  12. airflow/providers/fab/www/static/dist/manifest.json +13 -13
  13. {apache_airflow_providers_fab-2.4.1.dist-info → apache_airflow_providers_fab-2.4.2rc1.dist-info}/METADATA +9 -10
  14. {apache_airflow_providers_fab-2.4.1.dist-info → apache_airflow_providers_fab-2.4.2rc1.dist-info}/RECORD +31 -31
  15. airflow/providers/fab/migrations/versions/0001_1_4_0_placeholder_migration.py +0 -45
  16. /airflow/providers/fab/www/static/dist/{743.27a753a06671118f1c5c.js.LICENSE.txt → 743.fc7a7c6ef9d09365976e.js.LICENSE.txt} +0 -0
  17. /airflow/providers/fab/www/static/dist/{airflowDefaultTheme.56d4475fdae7883d3454.css → airflowDefaultTheme.ff5a35f322070b094aa2.css} +0 -0
  18. /airflow/providers/fab/www/static/dist/{airflowDefaultTheme.56d4475fdae7883d3454.js → airflowDefaultTheme.ff5a35f322070b094aa2.js} +0 -0
  19. /airflow/providers/fab/www/static/dist/{flash.0951d47c62bc8906be65.css → flash.5583a9e0cf11f2be93da.css} +0 -0
  20. /airflow/providers/fab/www/static/dist/{flash.0951d47c62bc8906be65.js → flash.5583a9e0cf11f2be93da.js} +0 -0
  21. /airflow/providers/fab/www/static/dist/{loadingDots.deaad0ce0e7691ed6251.css → loadingDots.2e5f555f0753107b0300.css} +0 -0
  22. /airflow/providers/fab/www/static/dist/{loadingDots.deaad0ce0e7691ed6251.js → loadingDots.2e5f555f0753107b0300.js} +0 -0
  23. /airflow/providers/fab/www/static/dist/{main.810554d06c3e30f2484e.css → main.3cf3be1a0c5439bb640d.css} +0 -0
  24. /airflow/providers/fab/www/static/dist/{main.810554d06c3e30f2484e.js.LICENSE.txt → main.3cf3be1a0c5439bb640d.js.LICENSE.txt} +0 -0
  25. /airflow/providers/fab/www/static/dist/{materialIcons.b0c6cc32cdacff89f7c2.css → materialIcons.3e67dd6fbfcc4f3b5105.css} +0 -0
  26. /airflow/providers/fab/www/static/dist/{materialIcons.b0c6cc32cdacff89f7c2.js → materialIcons.3e67dd6fbfcc4f3b5105.js} +0 -0
  27. /airflow/providers/fab/www/static/dist/{moment.518a43bcfaf149ae2836.js → moment.9baee5ec3d7639a10897.js} +0 -0
  28. /airflow/providers/fab/www/static/dist/{runtime.4a925577de9ab84d8e00.js → runtime.ad800fc1845ad5c6ddeb.js} +0 -0
  29. {apache_airflow_providers_fab-2.4.1.dist-info → apache_airflow_providers_fab-2.4.2rc1.dist-info}/WHEEL +0 -0
  30. {apache_airflow_providers_fab-2.4.1.dist-info → apache_airflow_providers_fab-2.4.2rc1.dist-info}/entry_points.txt +0 -0
  31. {apache_airflow_providers_fab-2.4.1.dist-info → apache_airflow_providers_fab-2.4.2rc1.dist-info}/licenses/3rd-party-licenses/LICENSES-ui.txt +0 -0
  32. {apache_airflow_providers_fab-2.4.1.dist-info → apache_airflow_providers_fab-2.4.2rc1.dist-info}/licenses/NOTICE +0 -0
@@ -29,7 +29,7 @@ from airflow import __version__ as airflow_version
29
29
 
30
30
  __all__ = ["__version__"]
31
31
 
32
- __version__ = "2.4.1"
32
+ __version__ = "2.4.2"
33
33
 
34
34
  if packaging.version.parse(packaging.version.parse(airflow_version).base_version) < packaging.version.parse(
35
35
  "3.0.2"
@@ -75,6 +75,7 @@ from airflow.providers.fab.www.extensions.init_views import (
75
75
  )
76
76
  from airflow.providers.fab.www.security import permissions
77
77
  from airflow.providers.fab.www.security.permissions import (
78
+ ACTION_CAN_READ,
78
79
  RESOURCE_AUDIT_LOG,
79
80
  RESOURCE_CLUSTER_ACTIVITY,
80
81
  RESOURCE_CONFIG,
@@ -82,6 +83,7 @@ from airflow.providers.fab.www.security.permissions import (
82
83
  RESOURCE_DAG,
83
84
  RESOURCE_DAG_CODE,
84
85
  RESOURCE_DAG_DEPENDENCIES,
86
+ RESOURCE_DAG_PREFIX,
85
87
  RESOURCE_DAG_RUN,
86
88
  RESOURCE_DAG_VERSION,
87
89
  RESOURCE_DAG_WARNING,
@@ -323,40 +325,52 @@ class FabAuthManager(BaseAuthManager[User]):
323
325
 
324
326
  There are multiple scenarios:
325
327
 
326
- 1. ``access_entity`` is not provided which means the user wants to access the DAG itself and not a sub
327
- entity (e.g. DAG runs).
328
- 2. ``access_entity`` is provided which means the user wants to access a sub entity of the DAG
329
- (e.g. DAG runs).
328
+ 1. ``method`` is "GET" and no details are provided which means the user wants to list Dags (or sub entities of Dags).
329
+ 2. ``access_entity`` is not provided which means the user wants to access the DAG itself and not a sub
330
+ entity (e.g. Task instances).
331
+ 3. ``access_entity`` is provided which means the user wants to access a sub entity of the DAG
332
+ (e.g. DAG runs).
330
333
 
331
334
  a. If ``method`` is GET, then check the user has READ permissions on the DAG and the sub entity.
332
- b. Else, check the user has EDIT permissions on the DAG and ``method`` on the sub entity. However,
333
- if no specific DAG is targeted, just check the sub entity.
335
+ b. Else, check the user has EDIT permissions on the DAG and ``method`` on the sub entity.
334
336
 
335
337
  :param method: The method to authorize.
336
338
  :param user: The user performing the action.
337
339
  :param access_entity: The dag access entity.
338
340
  :param details: The dag details.
339
341
  """
340
- if not access_entity:
342
+ if access_entity:
343
+ # If a sub-Dag entity is specified, check whether the user has access to it
344
+ resource_types = self._get_fab_resource_types(access_entity)
345
+ access_entity_authorized = all(
346
+ self._is_authorized(method=method, resource_type=resource_type, user=user)
347
+ for resource_type in resource_types
348
+ )
349
+ if access_entity == DagAccessEntity.RUN and details and details.id:
350
+ # Check using the deprecated permission prefix "DAG Run" to check whether the user has access to dag runs
351
+ is_authorized_run = self._is_authorized(
352
+ method=method,
353
+ resource_type=permissions.resource_name(details.id, RESOURCE_DAG_RUN),
354
+ user=user,
355
+ )
356
+ if not (is_authorized_run or access_entity_authorized):
357
+ return False
358
+ elif not access_entity_authorized:
359
+ return False
360
+
361
+ if method == "GET" and (not details or not details.id):
341
362
  # Scenario 1
363
+ return self._is_authorized_list_dags(user=user)
364
+ if not access_entity:
365
+ # Scenario 2
342
366
  return self._is_authorized_dag(method=method, details=details, user=user)
343
- # Scenario 2
344
- resource_types = self._get_fab_resource_types(access_entity)
367
+ # Scenario 3
345
368
  dag_method: ResourceMethod = "GET" if method == "GET" else "PUT"
346
-
347
369
  if (details and details.id) and not self._is_authorized_dag(
348
370
  method=dag_method, details=details, user=user
349
371
  ):
350
372
  return False
351
-
352
- return all(
353
- (
354
- self._is_authorized(method=method, resource_type=resource_type, user=user)
355
- if resource_type != RESOURCE_DAG_RUN or not hasattr(permissions, "resource_name")
356
- else self._is_authorized_dag_run(method=method, details=details, user=user)
357
- )
358
- for resource_type in resource_types
359
- )
373
+ return True
360
374
 
361
375
  def is_authorized_backfill(
362
376
  self,
@@ -545,7 +559,7 @@ class FabAuthManager(BaseAuthManager[User]):
545
559
 
546
560
  :param method: the method to perform
547
561
  :param resource_type: the type of resource the user attempts to perform the action on
548
- :param user: the user to performing the action
562
+ :param user: the user performing the action
549
563
 
550
564
  :meta private:
551
565
  """
@@ -565,7 +579,7 @@ class FabAuthManager(BaseAuthManager[User]):
565
579
 
566
580
  :param method: the method to perform
567
581
  :param details: details about the DAG
568
- :param user: the user to performing the action
582
+ :param user: the user performing the action
569
583
 
570
584
  :meta private:
571
585
  """
@@ -577,34 +591,25 @@ class FabAuthManager(BaseAuthManager[User]):
577
591
  # Check whether the user has permissions to access a specific DAG
578
592
  resource_dag_name = permissions.resource_name(details.id, RESOURCE_DAG)
579
593
  return self._is_authorized(method=method, resource_type=resource_dag_name, user=user)
580
- authorized_dags = self.get_authorized_dag_ids(user=user, method=method)
581
- return len(authorized_dags) > 0
594
+ return False
582
595
 
583
- def _is_authorized_dag_run(
584
- self,
585
- method: ResourceMethod,
586
- details: DagDetails | None,
587
- user: User,
588
- ) -> bool:
596
+ def _is_authorized_list_dags(self, *, user: User) -> bool:
589
597
  """
590
- Return whether the user is authorized to perform a given action on a DAG Run.
598
+ Return whether the user is authorized to list Dags.
591
599
 
592
- :param method: the method to perform
593
- :param details: details about the DAG
594
- :param user: the user to performing the action
600
+ :param user: the user performing the action
595
601
 
596
602
  :meta private:
597
603
  """
598
- is_global_authorized = self._is_authorized(method=method, resource_type=RESOURCE_DAG_RUN, user=user)
604
+ is_global_authorized = self._is_authorized(method="GET", resource_type=RESOURCE_DAG, user=user)
599
605
  if is_global_authorized:
600
606
  return True
601
607
 
602
- if details and details.id:
603
- # Check whether the user has permissions to access a specific DAG Run permission on a DAG Level
604
- resource_dag_name = permissions.resource_name(details.id, RESOURCE_DAG_RUN)
605
- return self._is_authorized(method=method, resource_type=resource_dag_name, user=user)
606
- authorized_dags = self.get_authorized_dag_ids(user=user, method=method)
607
- return len(authorized_dags) > 0
608
+ user_permissions = self._get_user_permissions(user)
609
+ for action, resource in user_permissions:
610
+ if action == ACTION_CAN_READ and resource.startswith(RESOURCE_DAG_PREFIX):
611
+ return True
612
+ return False
608
613
 
609
614
  @staticmethod
610
615
  def _get_fab_action(method: ExtendedResourceMethod) -> str:
@@ -288,8 +288,8 @@ class User(Model, BaseUser):
288
288
  }
289
289
  return self._perms
290
290
 
291
- def get_id(self):
292
- return self.id
291
+ def get_id(self) -> str:
292
+ return str(self.id)
293
293
 
294
294
  def get_name(self) -> str:
295
295
  return self.username or self.email or self.user_id
@@ -63,14 +63,13 @@ from flask_jwt_extended import JWTManager
63
63
  from flask_login import LoginManager
64
64
  from itsdangerous import want_bytes
65
65
  from markupsafe import Markup, escape
66
- from sqlalchemy import func, inspect, or_, select
66
+ from sqlalchemy import delete, func, inspect, or_, select
67
67
  from sqlalchemy.exc import MultipleResultsFound
68
68
  from sqlalchemy.orm import joinedload
69
69
  from werkzeug.security import check_password_hash, generate_password_hash
70
70
 
71
71
  from airflow.configuration import conf
72
72
  from airflow.exceptions import AirflowException
73
- from airflow.models import DagBag
74
73
  from airflow.providers.fab.auth_manager.models import (
75
74
  Action,
76
75
  Group,
@@ -101,6 +100,7 @@ from airflow.providers.fab.auth_manager.views.user_edit import (
101
100
  CustomUserInfoEditView,
102
101
  )
103
102
  from airflow.providers.fab.auth_manager.views.user_stats import CustomUserStatsChartView
103
+ from airflow.providers.fab.version_compat import AIRFLOW_V_3_1_PLUS
104
104
  from airflow.providers.fab.www.security import permissions
105
105
  from airflow.providers.fab.www.security_manager import AirflowSecurityManagerV2
106
106
  from airflow.providers.fab.www.session import AirflowDatabaseSessionInterface
@@ -111,12 +111,29 @@ if TYPE_CHECKING:
111
111
  RESOURCE_ASSET,
112
112
  RESOURCE_ASSET_ALIAS,
113
113
  )
114
+ from airflow.sdk import DAG
114
115
  else:
115
116
  from airflow.providers.common.compat.security.permissions import (
116
117
  RESOURCE_ASSET,
117
118
  RESOURCE_ASSET_ALIAS,
118
119
  )
119
120
 
121
+ if AIRFLOW_V_3_1_PLUS:
122
+ from airflow.models.dagbag import DBDagBag
123
+ from airflow.utils.session import create_session
124
+
125
+ def _iter_dags() -> Iterable[DAG]:
126
+ with create_session() as session:
127
+ yield from DBDagBag().iter_all_latest_version_dags(session=session)
128
+ else:
129
+ from airflow.models.dagbag import DagBag
130
+
131
+ def _iter_dags() -> Iterable[DAG]:
132
+ dagbag = DagBag(read_dags_from_db=True) # type: ignore[call-arg]
133
+ dagbag.collect_dags_from_db() # type: ignore[attr-defined]
134
+ return dagbag.dags.values()
135
+
136
+
120
137
  log = logging.getLogger(__name__)
121
138
 
122
139
  # This is the limit of DB user sessions that we consider as "healthy". If you have more sessions that this
@@ -548,7 +565,7 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
548
565
  interface = self.appbuilder.get_app.session_interface
549
566
  session = interface.db.session
550
567
  user_session_model = interface.sql_session_model
551
- num_sessions = session.query(user_session_model).count()
568
+ num_sessions = session.scalars(select(func.count()).select_from(user_session_model)).one()
552
569
  if num_sessions > MAX_NUM_DATABASE_USER_SESSIONS:
553
570
  safe_username = escape(user.username)
554
571
  self._cli_safe_flash(
@@ -563,7 +580,7 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
563
580
  "warning",
564
581
  )
565
582
  else:
566
- for s in session.query(user_session_model):
583
+ for s in session.scalars(select(user_session_model)).all():
567
584
  session_details = interface.serializer.loads(want_bytes(s.data))
568
585
  if session_details.get("_user_id") == user.id:
569
586
  session.delete(s)
@@ -938,11 +955,9 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
938
955
  if you only need to sync a single DAG.
939
956
  """
940
957
  perms = self.get_all_permissions()
941
- dagbag = DagBag(read_dags_from_db=True)
942
- dagbag.collect_dags_from_db()
943
- dags = dagbag.dags.values()
944
958
 
945
- for dag in dags:
959
+ for dag in _iter_dags():
960
+ print(dag)
946
961
  for resource_name, resource_values in self.RESOURCE_DETAILS_MAP.items():
947
962
  dag_resource_name = permissions.resource_name(dag.dag_id, resource_name)
948
963
  for action_name in resource_values["actions"]:
@@ -1194,12 +1209,14 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1194
1209
  """FAB leaves faulty permissions that need to be cleaned up."""
1195
1210
  self.log.debug("Cleaning faulty perms")
1196
1211
  sesh = self.appbuilder.get_session
1197
- perms = sesh.query(Permission).filter(
1198
- or_(
1199
- Permission.action == None, # noqa: E711
1200
- Permission.resource == None, # noqa: E711
1212
+ perms = sesh.scalars(
1213
+ select(Permission).where(
1214
+ or_(
1215
+ Permission.action == None, # noqa: E711
1216
+ Permission.resource == None, # noqa: E711
1217
+ )
1201
1218
  )
1202
- )
1219
+ ).all()
1203
1220
  # Since FAB doesn't define ON DELETE CASCADE on these tables, we need
1204
1221
  # to delete the _object_ so that SQLA knows to delete the many-to-many
1205
1222
  # relationship object too. :(
@@ -1277,10 +1294,10 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1277
1294
 
1278
1295
  :param name: the role name
1279
1296
  """
1280
- return self.get_session.query(self.role_model).filter_by(name=name).one_or_none()
1297
+ return self.get_session.scalars(select(self.role_model).filter_by(name=name)).unique().one_or_none()
1281
1298
 
1282
1299
  def get_all_roles(self):
1283
- return self.get_session.query(self.role_model).all()
1300
+ return self.get_session.scalars(select(self.role_model)).unique().all()
1284
1301
 
1285
1302
  def delete_role(self, role_name: str) -> None:
1286
1303
  """
@@ -1289,10 +1306,10 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1289
1306
  :param role_name: the name of a role in the ab_role table
1290
1307
  """
1291
1308
  session = self.get_session
1292
- role = session.query(Role).filter(Role.name == role_name).first()
1309
+ role = session.scalars(select(Role).where(Role.name == role_name)).first()
1293
1310
  if role:
1294
1311
  log.info("Deleting role '%s'", role_name)
1295
- session.delete(role)
1312
+ session.execute(delete(Role).where(Role.name == role_name))
1296
1313
  session.commit()
1297
1314
  else:
1298
1315
  raise AirflowException(f"Role named '{role_name}' does not exist")
@@ -1323,7 +1340,11 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1323
1340
  return _roles
1324
1341
 
1325
1342
  def get_public_role(self):
1326
- return self.get_session.query(self.role_model).filter_by(name=self.auth_role_public).one_or_none()
1343
+ return (
1344
+ self.get_session.scalars(select(self.role_model).filter_by(name=self.auth_role_public))
1345
+ .unique()
1346
+ .one_or_none()
1347
+ )
1327
1348
 
1328
1349
  """
1329
1350
  -----------
@@ -1380,7 +1401,7 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1380
1401
 
1381
1402
  def count_users(self):
1382
1403
  """Return the number of users in the database."""
1383
- return self.get_session.query(func.count(self.user_model.id)).scalar()
1404
+ return self.get_session.scalar(select(func.count(self.user_model.id)))
1384
1405
 
1385
1406
  def add_register_user(self, username, first_name, last_name, email, password="", hashed_password=""):
1386
1407
  """
@@ -1412,22 +1433,22 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1412
1433
  if username:
1413
1434
  try:
1414
1435
  if self.auth_username_ci:
1415
- return (
1416
- self.get_session.query(self.user_model)
1417
- .filter(func.lower(self.user_model.username) == func.lower(username))
1418
- .one_or_none()
1436
+ return self.get_session.scalars(
1437
+ select(self.user_model).where(
1438
+ func.lower(self.user_model.username) == func.lower(username)
1439
+ )
1440
+ ).one_or_none()
1441
+ return self.get_session.scalars(
1442
+ select(self.user_model).where(
1443
+ func.lower(self.user_model.username) == func.lower(username)
1419
1444
  )
1420
- return (
1421
- self.get_session.query(self.user_model)
1422
- .filter(func.lower(self.user_model.username) == func.lower(username))
1423
- .one_or_none()
1424
- )
1445
+ ).one_or_none()
1425
1446
  except MultipleResultsFound:
1426
1447
  log.error("Multiple results found for user %s", username)
1427
1448
  return None
1428
1449
  elif email:
1429
1450
  try:
1430
- return self.get_session.query(self.user_model).filter_by(email=email).one_or_none()
1451
+ return self.get_session.scalars(select(self.user_model).filter_by(email=email)).one_or_none()
1431
1452
  except MultipleResultsFound:
1432
1453
  log.error("Multiple results found for user with email %s", email)
1433
1454
  return None
@@ -1459,7 +1480,7 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1459
1480
  return False
1460
1481
 
1461
1482
  def get_all_users(self):
1462
- return self.get_session.query(self.user_model).all()
1483
+ return self.get_session.scalars(select(self.user_model)).all()
1463
1484
 
1464
1485
  def update_user_auth_stat(self, user, success=True):
1465
1486
  """
@@ -1499,7 +1520,7 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1499
1520
 
1500
1521
  :param name: name
1501
1522
  """
1502
- return self.get_session.query(self.action_model).filter_by(name=name).one_or_none()
1523
+ return self.get_session.scalars(select(self.action_model).filter_by(name=name)).one_or_none()
1503
1524
 
1504
1525
  def create_action(self, name):
1505
1526
  """
@@ -1532,11 +1553,9 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1532
1553
  log.warning(const.LOGMSG_WAR_SEC_DEL_PERMISSION, name)
1533
1554
  return False
1534
1555
  try:
1535
- perms = (
1536
- self.get_session.query(self.permission_model)
1537
- .filter(self.permission_model.action == action)
1538
- .all()
1539
- )
1556
+ perms = self.get_session.scalars(
1557
+ select(self.permission_model).where(self.permission_model.action == action)
1558
+ ).all()
1540
1559
  if perms:
1541
1560
  log.warning(const.LOGMSG_WAR_SEC_DEL_PERM_PVM, action, perms)
1542
1561
  return False
@@ -1560,7 +1579,7 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1560
1579
 
1561
1580
  :param name: Name of resource
1562
1581
  """
1563
- return self.get_session.query(self.resource_model).filter_by(name=name).one_or_none()
1582
+ return self.get_session.scalars(select(self.resource_model).filter_by(name=name)).one_or_none()
1564
1583
 
1565
1584
  def create_resource(self, name) -> Resource | None:
1566
1585
  """
@@ -1602,10 +1621,13 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1602
1621
  resource = self.get_resource(resource_name)
1603
1622
  if action and resource:
1604
1623
  return (
1605
- self.get_session.query(self.permission_model)
1606
- .filter_by(action=action, resource=resource)
1624
+ self.get_session.scalars(
1625
+ select(self.permission_model).filter_by(action=action, resource=resource)
1626
+ )
1627
+ .unique()
1607
1628
  .one_or_none()
1608
1629
  )
1630
+
1609
1631
  return None
1610
1632
 
1611
1633
  def get_resource_permissions(self, resource: Resource) -> Permission:
@@ -1614,7 +1636,9 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1614
1636
 
1615
1637
  :param resource: Object representing a single resource.
1616
1638
  """
1617
- return self.get_session.query(self.permission_model).filter_by(resource_id=resource.id).all()
1639
+ return self.get_session.scalars(
1640
+ select(self.permission_model).filter_by(resource_id=resource.id)
1641
+ ).all()
1618
1642
 
1619
1643
  def create_permission(self, action_name, resource_name) -> Permission | None:
1620
1644
  """
@@ -1661,9 +1685,9 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1661
1685
  perm = self.get_permission(action_name, resource_name)
1662
1686
  if not perm:
1663
1687
  return
1664
- roles = (
1665
- self.get_session.query(self.role_model).filter(self.role_model.permissions.contains(perm)).first()
1666
- )
1688
+ roles = self.get_session.scalars(
1689
+ select(self.role_model).where(self.role_model.permissions.contains(perm))
1690
+ ).first()
1667
1691
  if roles:
1668
1692
  log.warning(const.LOGMSG_WAR_SEC_DEL_PERMVIEW, resource_name, action_name, roles)
1669
1693
  return
@@ -1672,7 +1696,9 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
1672
1696
  self.get_session.delete(perm)
1673
1697
  self.get_session.commit()
1674
1698
  # if no more permission on permission view, delete permission
1675
- if not self.get_session.query(self.permission_model).filter_by(action=perm.action).all():
1699
+ if not self.get_session.scalars(
1700
+ select(self.permission_model).filter_by(action=perm.action)
1701
+ ).all():
1676
1702
  self.delete_action(perm.action.name)
1677
1703
  log.info(const.LOGMSG_INF_SEC_DEL_PERMVIEW, action_name, resource_name)
1678
1704
  except Exception as e:
@@ -2178,7 +2204,7 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
2178
2204
  def oauth_token_getter():
2179
2205
  """Get authentication (OAuth) token."""
2180
2206
  token = session.get("oauth")
2181
- log.debug("Token Get: %s", token)
2207
+ log.debug("OAuth token retrieved from session.")
2182
2208
  return token
2183
2209
 
2184
2210
  @staticmethod
@@ -30,6 +30,20 @@ def get_provider_info():
30
30
  "fab": {
31
31
  "description": "This section contains configs specific to FAB provider.",
32
32
  "options": {
33
+ "cookie_secure": {
34
+ "description": "Cookie with the secure attribute is only sent to the server with an HTTPS connection.\n",
35
+ "version_added": "2.4.0",
36
+ "type": "boolean",
37
+ "example": None,
38
+ "default": "False",
39
+ },
40
+ "cookie_samesite": {
41
+ "description": "Whether the cookie is restricted to a first-party or same-site context.\n",
42
+ "version_added": "2.4.0",
43
+ "type": "string",
44
+ "example": None,
45
+ "default": "Lax",
46
+ },
33
47
  "navbar_color": {
34
48
  "description": "Define the color of navigation bar\n",
35
49
  "version_added": "2.2.0",