apache-airflow-providers-fab 2.0.0b1__py3-none-any.whl → 2.0.0rc2__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 (43) hide show
  1. airflow/providers/fab/__init__.py +1 -1
  2. airflow/providers/fab/auth_manager/api/auth/backend/basic_auth.py +3 -4
  3. airflow/providers/fab/auth_manager/api/auth/backend/kerberos_auth.py +3 -3
  4. airflow/providers/fab/auth_manager/api/auth/backend/session.py +1 -1
  5. airflow/providers/fab/auth_manager/api_endpoints/role_and_permission_endpoint.py +8 -9
  6. airflow/providers/fab/auth_manager/api_endpoints/user_endpoint.py +7 -8
  7. airflow/providers/fab/auth_manager/api_fastapi/datamodels/login.py +1 -1
  8. airflow/providers/fab/auth_manager/api_fastapi/openapi/v1-generated.yaml +5 -4
  9. airflow/providers/fab/auth_manager/api_fastapi/routes/login.py +1 -1
  10. airflow/providers/fab/auth_manager/api_fastapi/services/login.py +5 -5
  11. airflow/providers/fab/auth_manager/cli_commands/db_command.py +1 -1
  12. airflow/providers/fab/auth_manager/cli_commands/utils.py +1 -1
  13. airflow/providers/fab/auth_manager/fab_auth_manager.py +4 -0
  14. airflow/providers/fab/auth_manager/models/db.py +2 -2
  15. airflow/providers/fab/auth_manager/openapi/v1.yaml +9 -0
  16. airflow/providers/fab/auth_manager/security_manager/override.py +0 -39
  17. airflow/providers/fab/get_provider_info.py +7 -3
  18. airflow/providers/fab/www/api_connexion/parameters.py +1 -1
  19. airflow/providers/fab/www/api_connexion/security.py +3 -3
  20. airflow/providers/fab/www/app.py +1 -1
  21. airflow/providers/fab/www/auth.py +6 -6
  22. airflow/providers/fab/www/package-lock.json +1428 -1904
  23. airflow/providers/fab/www/package.json +20 -24
  24. airflow/providers/fab/www/static/dist/48f0ea180c40270a5b05.png +1 -0
  25. airflow/providers/fab/www/static/dist/649c0b07771e68fafdeb.png +1 -0
  26. airflow/providers/fab/www/static/dist/airflowDefaultTheme.feec4a4075c2f3d6ae01.css +2 -2
  27. airflow/providers/fab/www/static/dist/f7490d556a6c42e49ba4.png +1 -0
  28. airflow/providers/fab/www/static/dist/jquery-ui.min.css +1 -1
  29. airflow/providers/fab/www/static/dist/main.edb2d40dfbbc537916e3.css +18 -0
  30. airflow/providers/fab/www/static/dist/{main.ec1d38d994d72bb083cd.js → main.edb2d40dfbbc537916e3.js} +1 -1
  31. airflow/providers/fab/www/static/dist/manifest.json +7 -4
  32. airflow/providers/fab/www/static/dist/materialIcons.57390fa60d8f61175334.css +1 -1
  33. airflow/providers/fab/www/static/dist/{moment.4d28b37c229bdfc54575.js → moment.624b1f00ba723d39ce06.js} +2 -2
  34. airflow/providers/fab/www/static/dist/{moment.4d28b37c229bdfc54575.js.LICENSE.txt → moment.624b1f00ba723d39ce06.js.LICENSE.txt} +1 -1
  35. airflow/providers/fab/www/static/dist/oss-licenses.json +2 -11
  36. airflow/providers/fab/www/views.py +1 -1
  37. airflow/providers/fab/www/webpack.config.js +2 -2
  38. {apache_airflow_providers_fab-2.0.0b1.dist-info → apache_airflow_providers_fab-2.0.0rc2.dist-info}/METADATA +18 -10
  39. {apache_airflow_providers_fab-2.0.0b1.dist-info → apache_airflow_providers_fab-2.0.0rc2.dist-info}/RECORD +42 -39
  40. airflow/providers/fab/www/static/dist/main.ec1d38d994d72bb083cd.css +0 -18
  41. /airflow/providers/fab/www/static/dist/{main.ec1d38d994d72bb083cd.js.LICENSE.txt → main.edb2d40dfbbc537916e3.js.LICENSE.txt} +0 -0
  42. {apache_airflow_providers_fab-2.0.0b1.dist-info → apache_airflow_providers_fab-2.0.0rc2.dist-info}/WHEEL +0 -0
  43. {apache_airflow_providers_fab-2.0.0b1.dist-info → apache_airflow_providers_fab-2.0.0rc2.dist-info}/entry_points.txt +0 -0
@@ -29,7 +29,7 @@ from airflow import __version__ as airflow_version
29
29
 
30
30
  __all__ = ["__version__"]
31
31
 
32
- __version__ = "2.0.0b1"
32
+ __version__ = "2.0.0"
33
33
 
34
34
  if packaging.version.parse(packaging.version.parse(airflow_version).base_version) < packaging.version.parse(
35
35
  "3.0.0.dev0"
@@ -26,9 +26,9 @@ from flask_appbuilder.const import AUTH_LDAP
26
26
  from flask_login import login_user
27
27
 
28
28
  from airflow.api_fastapi.app import get_auth_manager
29
- from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
30
29
 
31
30
  if TYPE_CHECKING:
31
+ from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
32
32
  from airflow.providers.fab.auth_manager.models import User
33
33
 
34
34
  CLIENT_AUTH: tuple[str, str] | Any | None = None
@@ -45,8 +45,7 @@ def auth_current_user() -> User | None:
45
45
  auth = request.authorization
46
46
  if auth is None or not auth.username or not auth.password:
47
47
  return None
48
-
49
- security_manager = cast(FabAuthManager, get_auth_manager()).security_manager
48
+ security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
50
49
  user = None
51
50
  if security_manager.auth_type == AUTH_LDAP:
52
51
  user = security_manager.auth_user_ldap(auth.username, auth.password)
@@ -67,4 +66,4 @@ def requires_authentication(function: T):
67
66
  else:
68
67
  return Response("Unauthorized", 401, {"WWW-Authenticate": "Basic"})
69
68
 
70
- return cast(T, decorated)
69
+ return cast("T", decorated)
@@ -28,11 +28,11 @@ from requests_kerberos import HTTPKerberosAuth
28
28
 
29
29
  from airflow.api_fastapi.app import get_auth_manager
30
30
  from airflow.configuration import conf
31
- from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
32
31
  from airflow.utils.net import getfqdn
33
32
 
34
33
  if TYPE_CHECKING:
35
34
  from airflow.api_fastapi.auth.managers.models.base_user import BaseUser
35
+ from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
36
36
 
37
37
  log = logging.getLogger(__name__)
38
38
 
@@ -115,7 +115,7 @@ T = TypeVar("T", bound=Callable)
115
115
 
116
116
 
117
117
  def find_user(username=None, email=None):
118
- security_manager = cast(FabAuthManager, get_auth_manager()).security_manager
118
+ security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
119
119
  return security_manager.find_user(username=username, email=email)
120
120
 
121
121
 
@@ -143,4 +143,4 @@ def requires_authentication(function: T, find_user: Callable[[str], BaseUser] |
143
143
  return _forbidden()
144
144
  return _unauthorized()
145
145
 
146
- return cast(T, decorated)
146
+ return cast("T", decorated)
@@ -44,4 +44,4 @@ def requires_authentication(function: T):
44
44
  return Response("Unauthorized", 401, {})
45
45
  return function(*args, **kwargs)
46
46
 
47
- return cast(T, decorated)
47
+ return cast("T", decorated)
@@ -25,7 +25,6 @@ from marshmallow import ValidationError
25
25
  from sqlalchemy import asc, desc, func, select
26
26
 
27
27
  from airflow.api_fastapi.app import get_auth_manager
28
- from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
29
28
  from airflow.providers.fab.auth_manager.models import Action, Role
30
29
  from airflow.providers.fab.auth_manager.schemas.role_and_permission_schema import (
31
30
  ActionCollection,
@@ -40,6 +39,7 @@ from airflow.providers.fab.www.api_connexion.security import requires_access_cus
40
39
  from airflow.providers.fab.www.security import permissions
41
40
 
42
41
  if TYPE_CHECKING:
42
+ from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
43
43
  from airflow.providers.fab.auth_manager.security_manager.override import FabAirflowSecurityManagerOverride
44
44
  from airflow.providers.fab.www.api_connexion.types import APIResponse, UpdateMask
45
45
 
@@ -60,7 +60,7 @@ def _check_action_and_resource(sm: FabAirflowSecurityManagerOverride, perms: lis
60
60
  @requires_access_custom_view("GET", permissions.RESOURCE_ROLE)
61
61
  def get_role(*, role_name: str) -> APIResponse:
62
62
  """Get role."""
63
- security_manager = cast(FabAuthManager, get_auth_manager()).security_manager
63
+ security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
64
64
  role = security_manager.find_role(name=role_name)
65
65
  if not role:
66
66
  raise NotFound(title="Role not found", detail=f"Role with name {role_name!r} was not found")
@@ -71,7 +71,7 @@ def get_role(*, role_name: str) -> APIResponse:
71
71
  @format_parameters({"limit": check_limit})
72
72
  def get_roles(*, order_by: str = "name", limit: int, offset: int | None = None) -> APIResponse:
73
73
  """Get roles."""
74
- security_manager = cast(FabAuthManager, get_auth_manager()).security_manager
74
+ security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
75
75
  session = security_manager.get_session
76
76
  total_entries = session.scalars(select(func.count(Role.id))).one()
77
77
  direction = desc if order_by.startswith("-") else asc
@@ -81,8 +81,7 @@ def get_roles(*, order_by: str = "name", limit: int, offset: int | None = None)
81
81
  allowed_sort_attrs = ["role_id", "name"]
82
82
  if order_by not in allowed_sort_attrs:
83
83
  raise BadRequest(
84
- detail=f"Ordering with '{order_by}' is disallowed or "
85
- f"the attribute does not exist on the model"
84
+ detail=f"Ordering with '{order_by}' is disallowed or the attribute does not exist on the model"
86
85
  )
87
86
 
88
87
  query = select(Role)
@@ -99,7 +98,7 @@ def get_roles(*, order_by: str = "name", limit: int, offset: int | None = None)
99
98
  @format_parameters({"limit": check_limit})
100
99
  def get_permissions(*, limit: int, offset: int | None = None) -> APIResponse:
101
100
  """Get permissions."""
102
- security_manager = cast(FabAuthManager, get_auth_manager()).security_manager
101
+ security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
103
102
  session = security_manager.get_session
104
103
  total_entries = session.scalars(select(func.count(Action.id))).one()
105
104
  query = select(Action)
@@ -110,7 +109,7 @@ def get_permissions(*, limit: int, offset: int | None = None) -> APIResponse:
110
109
  @requires_access_custom_view("DELETE", permissions.RESOURCE_ROLE)
111
110
  def delete_role(*, role_name: str) -> APIResponse:
112
111
  """Delete a role."""
113
- security_manager = cast(FabAuthManager, get_auth_manager()).security_manager
112
+ security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
114
113
 
115
114
  role = security_manager.find_role(name=role_name)
116
115
  if not role:
@@ -122,7 +121,7 @@ def delete_role(*, role_name: str) -> APIResponse:
122
121
  @requires_access_custom_view("PUT", permissions.RESOURCE_ROLE)
123
122
  def patch_role(*, role_name: str, update_mask: UpdateMask = None) -> APIResponse:
124
123
  """Update a role."""
125
- security_manager = cast(FabAuthManager, get_auth_manager()).security_manager
124
+ security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
126
125
  body = request.json
127
126
  try:
128
127
  data = role_schema.load(body)
@@ -155,7 +154,7 @@ def patch_role(*, role_name: str, update_mask: UpdateMask = None) -> APIResponse
155
154
  @requires_access_custom_view("POST", permissions.RESOURCE_ROLE)
156
155
  def post_role() -> APIResponse:
157
156
  """Create a new role."""
158
- security_manager = cast(FabAuthManager, get_auth_manager()).security_manager
157
+ security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
159
158
  body = request.json
160
159
  try:
161
160
  data = role_schema.load(body)
@@ -26,7 +26,6 @@ from sqlalchemy import asc, desc, func, select
26
26
  from werkzeug.security import generate_password_hash
27
27
 
28
28
  from airflow.api_fastapi.app import get_auth_manager
29
- from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
30
29
  from airflow.providers.fab.auth_manager.models import User
31
30
  from airflow.providers.fab.auth_manager.schemas.user_schema import (
32
31
  UserCollection,
@@ -40,6 +39,7 @@ from airflow.providers.fab.www.api_connexion.security import requires_access_cus
40
39
  from airflow.providers.fab.www.security import permissions
41
40
 
42
41
  if TYPE_CHECKING:
42
+ from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
43
43
  from airflow.providers.fab.auth_manager.models import Role
44
44
  from airflow.providers.fab.www.api_connexion.types import APIResponse, UpdateMask
45
45
 
@@ -47,7 +47,7 @@ if TYPE_CHECKING:
47
47
  @requires_access_custom_view("GET", permissions.RESOURCE_USER)
48
48
  def get_user(*, username: str) -> APIResponse:
49
49
  """Get a user."""
50
- security_manager = cast(FabAuthManager, get_auth_manager()).security_manager
50
+ security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
51
51
  user = security_manager.find_user(username=username)
52
52
  if not user:
53
53
  raise NotFound(title="User not found", detail=f"The User with username `{username}` was not found")
@@ -58,7 +58,7 @@ def get_user(*, username: str) -> APIResponse:
58
58
  @format_parameters({"limit": check_limit})
59
59
  def get_users(*, limit: int, order_by: str = "id", offset: str | None = None) -> APIResponse:
60
60
  """Get users."""
61
- security_manager = cast(FabAuthManager, get_auth_manager()).security_manager
61
+ security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
62
62
  session = security_manager.get_session
63
63
  total_entries = session.execute(select(func.count(User.id))).scalar()
64
64
  direction = desc if order_by.startswith("-") else asc
@@ -76,8 +76,7 @@ def get_users(*, limit: int, order_by: str = "id", offset: str | None = None) ->
76
76
  ]
77
77
  if order_by not in allowed_sort_attrs:
78
78
  raise BadRequest(
79
- detail=f"Ordering with '{order_by}' is disallowed or "
80
- f"the attribute does not exist on the model"
79
+ detail=f"Ordering with '{order_by}' is disallowed or the attribute does not exist on the model"
81
80
  )
82
81
 
83
82
  query = select(User).order_by(direction(getattr(User, order_param))).offset(offset).limit(limit)
@@ -94,7 +93,7 @@ def post_user() -> APIResponse:
94
93
  except ValidationError as e:
95
94
  raise BadRequest(detail=str(e.messages))
96
95
 
97
- security_manager = cast(FabAuthManager, get_auth_manager()).security_manager
96
+ security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
98
97
  username = data["username"]
99
98
  email = data["email"]
100
99
 
@@ -137,7 +136,7 @@ def patch_user(*, username: str, update_mask: UpdateMask = None) -> APIResponse:
137
136
  except ValidationError as e:
138
137
  raise BadRequest(detail=str(e.messages))
139
138
 
140
- security_manager = cast(FabAuthManager, get_auth_manager()).security_manager
139
+ security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
141
140
 
142
141
  user = security_manager.find_user(username=username)
143
142
  if user is None:
@@ -201,7 +200,7 @@ def patch_user(*, username: str, update_mask: UpdateMask = None) -> APIResponse:
201
200
  @requires_access_custom_view("DELETE", permissions.RESOURCE_USER)
202
201
  def delete_user(*, username: str) -> APIResponse:
203
202
  """Delete a user."""
204
- security_manager = cast(FabAuthManager, get_auth_manager()).security_manager
203
+ security_manager = cast("FabAuthManager", get_auth_manager()).security_manager
205
204
 
206
205
  user = security_manager.find_user(username=username)
207
206
  if user is None:
@@ -22,7 +22,7 @@ from airflow.api_fastapi.core_api.base import BaseModel
22
22
  class LoginResponse(BaseModel):
23
23
  """API Token serializer for responses."""
24
24
 
25
- jwt_token: str
25
+ access_token: str
26
26
 
27
27
 
28
28
  class LoginBody(BaseModel):
@@ -89,7 +89,8 @@ components:
89
89
  detail:
90
90
  anyOf:
91
91
  - type: string
92
- - type: object
92
+ - additionalProperties: true
93
+ type: object
93
94
  title: Detail
94
95
  type: object
95
96
  required:
@@ -121,12 +122,12 @@ components:
121
122
  description: API Token serializer for requests.
122
123
  LoginResponse:
123
124
  properties:
124
- jwt_token:
125
+ access_token:
125
126
  type: string
126
- title: Jwt Token
127
+ title: Access Token
127
128
  type: object
128
129
  required:
129
- - jwt_token
130
+ - access_token
130
131
  title: LoginResponse
131
132
  description: API Token serializer for responses.
132
133
  ValidationError:
@@ -47,5 +47,5 @@ def create_token(body: LoginBody) -> LoginResponse:
47
47
  def create_token_cli(body: LoginBody) -> LoginResponse:
48
48
  """Generate a new CLI API token."""
49
49
  return FABAuthManagerLogin.create_token(
50
- body=body, expiration_time_in_sec=conf.getint("api_auth", "jwt_cli_expiration_time")
50
+ body=body, expiration_time_in_seconds=conf.getint("api_auth", "jwt_cli_expiration_time")
51
51
  )
@@ -24,9 +24,9 @@ from starlette.exceptions import HTTPException
24
24
  from airflow.api_fastapi.app import get_auth_manager
25
25
  from airflow.configuration import conf
26
26
  from airflow.providers.fab.auth_manager.api_fastapi.datamodels.login import LoginBody, LoginResponse
27
- from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
28
27
 
29
28
  if TYPE_CHECKING:
29
+ from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
30
30
  from airflow.providers.fab.auth_manager.models import User
31
31
 
32
32
 
@@ -35,7 +35,7 @@ class FABAuthManagerLogin:
35
35
 
36
36
  @classmethod
37
37
  def create_token(
38
- cls, body: LoginBody, expiration_time_in_sec: int = conf.getint("api_auth", "jwt_expiration_time")
38
+ cls, body: LoginBody, expiration_time_in_seconds: int = conf.getint("api_auth", "jwt_expiration_time")
39
39
  ) -> LoginResponse:
40
40
  """Create a new token."""
41
41
  if not body.username or not body.password:
@@ -43,15 +43,15 @@ class FABAuthManagerLogin:
43
43
  status_code=status.HTTP_400_BAD_REQUEST, detail="Username and password must be provided"
44
44
  )
45
45
 
46
- auth_manager = cast(FabAuthManager, get_auth_manager())
46
+ auth_manager = cast("FabAuthManager", get_auth_manager())
47
47
  user: User = auth_manager.security_manager.find_user(username=body.username)
48
48
  if not user:
49
49
  raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid username")
50
50
 
51
51
  if auth_manager.security_manager.check_password(username=body.username, password=body.password):
52
52
  return LoginResponse(
53
- jwt_token=auth_manager.generate_jwt(
54
- user=user, expiration_time_in_seconds=expiration_time_in_sec
53
+ access_token=auth_manager.generate_jwt(
54
+ user=user, expiration_time_in_seconds=expiration_time_in_seconds
55
55
  )
56
56
  )
57
57
  else:
@@ -17,7 +17,7 @@
17
17
  from __future__ import annotations
18
18
 
19
19
  from airflow import settings
20
- from airflow.cli.commands.local_commands.db_command import run_db_downgrade_command, run_db_migrate_command
20
+ from airflow.cli.commands.db_command import run_db_downgrade_command, run_db_migrate_command
21
21
  from airflow.providers.fab.auth_manager.models.db import _REVISION_HEADS_MAP, FABDBManager
22
22
  from airflow.utils import cli as cli_utils
23
23
  from airflow.utils.providers_configuration_loader import providers_configuration_loaded
@@ -59,7 +59,7 @@ def get_application_builder() -> Generator[AirflowAppBuilder, None, None]:
59
59
  url = make_url(flask_app.config["SQLALCHEMY_DATABASE_URI"])
60
60
  if url.drivername == "sqlite" and url.database and not isabs(url.database):
61
61
  raise AirflowConfigException(
62
- f'Cannot use relative path: `{conf.get("database", "SQL_ALCHEMY_CONN")}` to connect to sqlite. '
62
+ f"Cannot use relative path: `{conf.get('database', 'SQL_ALCHEMY_CONN')}` to connect to sqlite. "
63
63
  "Please use absolute path such as `sqlite:////tmp/airflow.db`."
64
64
  )
65
65
  flask_app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
@@ -521,6 +521,10 @@ class FabAuthManager(BaseAuthManager[User]):
521
521
  if self._is_authorized(method="MENU", resource_type=item["resource_type"], user=user)
522
522
  ]
523
523
 
524
+ @staticmethod
525
+ def get_db_manager() -> str | None:
526
+ return "airflow.providers.fab.auth_manager.models.db.FABDBManager"
527
+
524
528
  def _is_authorized(
525
529
  self,
526
530
  *,
@@ -54,8 +54,8 @@ class FABDBManager(BaseDBManager):
54
54
  alembic_file = (PACKAGE_DIR / "alembic.ini").as_posix()
55
55
  supports_table_dropping = True
56
56
 
57
- def _create_db_from_orm(self):
58
- super()._create_db_from_orm()
57
+ def create_db_from_orm(self):
58
+ super().create_db_from_orm()
59
59
  _get_flask_db(settings.SQL_ALCHEMY_CONN).create_all()
60
60
 
61
61
  def upgradedb(self, to_revision=None, from_revision=None, show_sql_only=False):
@@ -687,12 +687,21 @@ components:
687
687
  Basic:
688
688
  type: http
689
689
  scheme: basic
690
+ description: To authenticate FAB auth manager API requests, clients have the option to use basic
691
+ authentication. To learn more about FAB auth manager API authentication, please read
692
+ https://airflow.apache.org/docs/apache-airflow-providers-fab/stable/auth-manager/api-authentication.html#basic-authentication.
690
693
  GoogleOpenId:
691
694
  type: openIdConnect
692
695
  openIdConnectUrl: https://accounts.google.com/.well-known/openid-configuration
696
+ description: To authenticate FAB auth manager API requests, clients have the option to use Google OpenID.
697
+ To learn more about Google OpenID authentication, please read
698
+ https://airflow.apache.org/docs/apache-airflow-providers-google/stable/api-auth-backend/google-openid.html.
693
699
  Kerberos:
694
700
  type: http
695
701
  scheme: negotiate
702
+ description: To authenticate FAB auth manager API requests, clients have the option to use Kerberos
703
+ authentication. To learn more about FAB auth manager API authentication, please read
704
+ https://airflow.apache.org/docs/apache-airflow-providers-fab/stable/auth-manager/api-authentication.html#kerberos-authentication.
696
705
 
697
706
  tags:
698
707
  - name: Role
@@ -21,8 +21,6 @@ import copy
21
21
  import datetime
22
22
  import itertools
23
23
  import logging
24
- import os
25
- import random
26
24
  import uuid
27
25
  from collections.abc import Collection, Iterable, Mapping
28
26
  from typing import TYPE_CHECKING, Any
@@ -740,43 +738,6 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
740
738
  """Get the builtin roles."""
741
739
  return self._builtin_roles
742
740
 
743
- def create_admin_standalone(self) -> tuple[str | None, str | None]:
744
- """Create an Admin user with a random password so that users can access airflow."""
745
- from airflow.configuration import AIRFLOW_HOME, make_group_other_inaccessible
746
-
747
- user_name = "admin"
748
-
749
- # We want a streamlined first-run experience, but we do not want to
750
- # use a preset password as people will inevitably run this on a public
751
- # server. Thus, we make a random password and store it in AIRFLOW_HOME,
752
- # with the reasoning that if you can read that directory, you can see
753
- # the database credentials anyway.
754
- password_path = os.path.join(AIRFLOW_HOME, "standalone_admin_password.txt")
755
-
756
- user_exists = self.find_user(user_name) is not None
757
- we_know_password = os.path.isfile(password_path)
758
-
759
- # If the user does not exist, make a random password and make it
760
- if not user_exists:
761
- print(f"FlaskAppBuilder Authentication Manager: Creating {user_name} user")
762
- if (role := self.find_role("Admin")) is None:
763
- raise AirflowException("Unable to find role 'Admin'")
764
- # password does not contain visually similar characters: ijlIJL1oO0
765
- password = "".join(random.choices("abcdefghkmnpqrstuvwxyzABCDEFGHKMNPQRSTUVWXYZ23456789", k=16))
766
- with open(password_path, "w") as file:
767
- file.write(password)
768
- make_group_other_inaccessible(password_path)
769
- self.add_user(user_name, "Admin", "User", "admin@example.com", role, password)
770
- print(f"FlaskAppBuilder Authentication Manager: Created {user_name} user")
771
- # If the user does exist, and we know its password, read the password
772
- elif user_exists and we_know_password:
773
- with open(password_path) as file:
774
- password = file.read().strip()
775
- # Otherwise we don't know the password
776
- else:
777
- password = None
778
- return user_name, password
779
-
780
741
  def _init_config(self):
781
742
  """
782
743
  Initialize config.
@@ -29,7 +29,7 @@ def get_provider_info():
29
29
  "state": "not-ready",
30
30
  "source-date-epoch": 1741121873,
31
31
  "versions": [
32
- "2.0.0b1",
32
+ "2.0.0",
33
33
  "1.5.2",
34
34
  "1.5.1",
35
35
  "1.5.0",
@@ -86,12 +86,16 @@ def get_provider_info():
86
86
  "dependencies": [
87
87
  "apache-airflow>=3.0.0.dev0",
88
88
  "apache-airflow-providers-common-compat>=1.2.1",
89
- "flask>=2.2,<2.3",
89
+ "blinker>=1.6.2",
90
+ "flask>=2.2.1,<2.3",
90
91
  "flask-appbuilder==4.5.3",
91
92
  "flask-login>=0.6.2",
93
+ "flask-session>=0.4.0,<0.6",
94
+ "flask-wtf>=1.1.0",
92
95
  "connexion[flask]>=2.14.2,<3.0",
93
96
  "jmespath>=0.7.0",
97
+ "werkzeug>=2.2,<4",
94
98
  ],
95
99
  "optional-dependencies": {"kerberos": ["kerberos>=1.3.0"]},
96
- "devel-dependencies": ["kerberos>=1.3.0"],
100
+ "devel-dependencies": ["kerberos>=1.3.0", "requests_kerberos>=0.14.0"],
97
101
  }
@@ -104,7 +104,7 @@ def format_parameters(params_formatters: dict[str, Callable[[Any], Any]]) -> Cal
104
104
  kwargs[key] = formatter(kwargs[key])
105
105
  return func(*args, **kwargs)
106
106
 
107
- return cast(T, wrapped_function)
107
+ return cast("T", wrapped_function)
108
108
 
109
109
  return format_parameters_decorator
110
110
 
@@ -22,18 +22,18 @@ from typing import TYPE_CHECKING, Callable, TypeVar, cast
22
22
  from flask import Response, current_app
23
23
 
24
24
  from airflow.api_fastapi.app import get_auth_manager
25
- from airflow.providers.fab.www.airflow_flask_app import AirflowApp
26
25
  from airflow.providers.fab.www.api_connexion.exceptions import PermissionDenied, Unauthenticated
27
26
 
28
27
  if TYPE_CHECKING:
29
28
  from airflow.api_fastapi.auth.managers.base_auth_manager import ResourceMethod
29
+ from airflow.providers.fab.www.airflow_flask_app import AirflowApp
30
30
 
31
31
  T = TypeVar("T", bound=Callable)
32
32
 
33
33
 
34
34
  def check_authentication() -> None:
35
35
  """Check that the request has valid authorization information."""
36
- for auth in cast(AirflowApp, current_app).api_auth:
36
+ for auth in cast("AirflowApp", current_app).api_auth:
37
37
  response = auth.requires_authentication(Response)()
38
38
  if response.status_code == 200:
39
39
  return
@@ -79,6 +79,6 @@ def requires_access_custom_view(
79
79
  kwargs=kwargs,
80
80
  )
81
81
 
82
- return cast(T, decorated)
82
+ return cast("T", decorated)
83
83
 
84
84
  return requires_access_decorator
@@ -60,7 +60,7 @@ def create_app(enable_plugins: bool):
60
60
  url = make_url(flask_app.config["SQLALCHEMY_DATABASE_URI"])
61
61
  if url.drivername == "sqlite" and url.database and not isabs(url.database):
62
62
  raise AirflowConfigException(
63
- f'Cannot use relative path: `{conf.get("database", "SQL_ALCHEMY_CONN")}` to connect to sqlite. '
63
+ f"Cannot use relative path: `{conf.get('database', 'SQL_ALCHEMY_CONN')}` to connect to sqlite. "
64
64
  "Please use absolute path such as `sqlite:////tmp/airflow.db`."
65
65
  )
66
66
 
@@ -121,7 +121,7 @@ def _has_access_no_details(is_authorized_callback: Callable[[], bool]) -> Callab
121
121
  kwargs=kwargs,
122
122
  )
123
123
 
124
- return cast(T, decorated)
124
+ return cast("T", decorated)
125
125
 
126
126
  return has_access_decorator
127
127
 
@@ -189,7 +189,7 @@ def has_access_connection(method: ResourceMethod) -> Callable[[T], T]:
189
189
  kwargs=kwargs,
190
190
  )
191
191
 
192
- return cast(T, decorated)
192
+ return cast("T", decorated)
193
193
 
194
194
  return has_access_decorator
195
195
 
@@ -240,7 +240,7 @@ def has_access_dag(method: ResourceMethod, access_entity: DagAccessEntity | None
240
240
  kwargs=kwargs,
241
241
  )
242
242
 
243
- return cast(T, decorated)
243
+ return cast("T", decorated)
244
244
 
245
245
  return has_access_decorator
246
246
 
@@ -269,7 +269,7 @@ def has_access_dag_entities(method: ResourceMethod, access_entity: DagAccessEnti
269
269
  kwargs=kwargs,
270
270
  )
271
271
 
272
- return cast(T, decorated)
272
+ return cast("T", decorated)
273
273
 
274
274
  return has_access_decorator
275
275
 
@@ -303,7 +303,7 @@ def has_access_pool(method: ResourceMethod) -> Callable[[T], T]:
303
303
  kwargs=kwargs,
304
304
  )
305
305
 
306
- return cast(T, decorated)
306
+ return cast("T", decorated)
307
307
 
308
308
  return has_access_decorator
309
309
 
@@ -336,7 +336,7 @@ def has_access_variable(method: ResourceMethod) -> Callable[[T], T]:
336
336
  kwargs=kwargs,
337
337
  )
338
338
 
339
- return cast(T, decorated)
339
+ return cast("T", decorated)
340
340
 
341
341
  return has_access_decorator
342
342