apache-airflow-providers-fab 1.0.2.dev0__py3-none-any.whl → 1.0.2.dev2__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.
- airflow/providers/fab/auth_manager/api/auth/backend/basic_auth.py +1 -0
- airflow/providers/fab/auth_manager/api_endpoints/role_and_permission_endpoint.py +7 -8
- airflow/providers/fab/auth_manager/api_endpoints/user_endpoint.py +5 -5
- airflow/providers/fab/auth_manager/cli_commands/role_command.py +1 -0
- airflow/providers/fab/auth_manager/cli_commands/sync_perm_command.py +1 -0
- airflow/providers/fab/auth_manager/cli_commands/user_command.py +1 -0
- airflow/providers/fab/auth_manager/decorators/auth.py +6 -3
- airflow/providers/fab/auth_manager/fab_auth_manager.py +8 -14
- airflow/providers/fab/auth_manager/security_manager/override.py +79 -13
- airflow/providers/fab/get_provider_info.py +2 -2
- {apache_airflow_providers_fab-1.0.2.dev0.dist-info → apache_airflow_providers_fab-1.0.2.dev2.dist-info}/METADATA +7 -6
- {apache_airflow_providers_fab-1.0.2.dev0.dist-info → apache_airflow_providers_fab-1.0.2.dev2.dist-info}/RECORD +14 -14
- {apache_airflow_providers_fab-1.0.2.dev0.dist-info → apache_airflow_providers_fab-1.0.2.dev2.dist-info}/WHEEL +0 -0
- {apache_airflow_providers_fab-1.0.2.dev0.dist-info → apache_airflow_providers_fab-1.0.2.dev2.dist-info}/entry_points.txt +0 -0
@@ -41,10 +41,9 @@ from airflow.www.extensions.init_auth_manager import get_auth_manager
|
|
41
41
|
|
42
42
|
if TYPE_CHECKING:
|
43
43
|
from airflow.api_connexion.types import APIResponse, UpdateMask
|
44
|
-
from airflow.www.security_manager import AirflowSecurityManagerV2
|
45
44
|
|
46
45
|
|
47
|
-
def _check_action_and_resource(sm:
|
46
|
+
def _check_action_and_resource(sm: FabAirflowSecurityManagerOverride, perms: list[tuple[str, str]]) -> None:
|
48
47
|
"""
|
49
48
|
Check if the action or resource exists and otherwise raise 400.
|
50
49
|
|
@@ -57,7 +56,7 @@ def _check_action_and_resource(sm: AirflowSecurityManagerV2, perms: list[tuple[s
|
|
57
56
|
raise BadRequest(detail=f"The specified resource: {resource!r} was not found")
|
58
57
|
|
59
58
|
|
60
|
-
@requires_access_custom_view(
|
59
|
+
@requires_access_custom_view("GET", permissions.RESOURCE_ROLE)
|
61
60
|
def get_role(*, role_name: str) -> APIResponse:
|
62
61
|
"""Get role."""
|
63
62
|
security_manager = cast(FabAirflowSecurityManagerOverride, get_auth_manager().security_manager)
|
@@ -67,7 +66,7 @@ def get_role(*, role_name: str) -> APIResponse:
|
|
67
66
|
return role_schema.dump(role)
|
68
67
|
|
69
68
|
|
70
|
-
@requires_access_custom_view(
|
69
|
+
@requires_access_custom_view("GET", permissions.RESOURCE_ROLE)
|
71
70
|
@format_parameters({"limit": check_limit})
|
72
71
|
def get_roles(*, order_by: str = "name", limit: int, offset: int | None = None) -> APIResponse:
|
73
72
|
"""Get roles."""
|
@@ -95,7 +94,7 @@ def get_roles(*, order_by: str = "name", limit: int, offset: int | None = None)
|
|
95
94
|
return role_collection_schema.dump(RoleCollection(roles=roles, total_entries=total_entries))
|
96
95
|
|
97
96
|
|
98
|
-
@requires_access_custom_view(
|
97
|
+
@requires_access_custom_view("GET", permissions.RESOURCE_ACTION)
|
99
98
|
@format_parameters({"limit": check_limit})
|
100
99
|
def get_permissions(*, limit: int, offset: int | None = None) -> APIResponse:
|
101
100
|
"""Get permissions."""
|
@@ -107,7 +106,7 @@ def get_permissions(*, limit: int, offset: int | None = None) -> APIResponse:
|
|
107
106
|
return action_collection_schema.dump(ActionCollection(actions=actions, total_entries=total_entries))
|
108
107
|
|
109
108
|
|
110
|
-
@requires_access_custom_view(
|
109
|
+
@requires_access_custom_view("DELETE", permissions.RESOURCE_ROLE)
|
111
110
|
def delete_role(*, role_name: str) -> APIResponse:
|
112
111
|
"""Delete a role."""
|
113
112
|
security_manager = cast(FabAirflowSecurityManagerOverride, get_auth_manager().security_manager)
|
@@ -119,7 +118,7 @@ def delete_role(*, role_name: str) -> APIResponse:
|
|
119
118
|
return NoContent, HTTPStatus.NO_CONTENT
|
120
119
|
|
121
120
|
|
122
|
-
@requires_access_custom_view(
|
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
124
|
security_manager = cast(FabAirflowSecurityManagerOverride, get_auth_manager().security_manager)
|
@@ -152,7 +151,7 @@ def patch_role(*, role_name: str, update_mask: UpdateMask = None) -> APIResponse
|
|
152
151
|
return role_schema.dump(role)
|
153
152
|
|
154
153
|
|
155
|
-
@requires_access_custom_view(
|
154
|
+
@requires_access_custom_view("POST", permissions.RESOURCE_ROLE)
|
156
155
|
def post_role() -> APIResponse:
|
157
156
|
"""Create a new role."""
|
158
157
|
security_manager = cast(FabAirflowSecurityManagerOverride, get_auth_manager().security_manager)
|
@@ -44,7 +44,7 @@ if TYPE_CHECKING:
|
|
44
44
|
from airflow.providers.fab.auth_manager.models import Role
|
45
45
|
|
46
46
|
|
47
|
-
@requires_access_custom_view(
|
47
|
+
@requires_access_custom_view("GET", permissions.RESOURCE_USER)
|
48
48
|
def get_user(*, username: str) -> APIResponse:
|
49
49
|
"""Get a user."""
|
50
50
|
security_manager = cast(FabAirflowSecurityManagerOverride, get_auth_manager().security_manager)
|
@@ -54,7 +54,7 @@ def get_user(*, username: str) -> APIResponse:
|
|
54
54
|
return user_collection_item_schema.dump(user)
|
55
55
|
|
56
56
|
|
57
|
-
@requires_access_custom_view(
|
57
|
+
@requires_access_custom_view("GET", permissions.RESOURCE_USER)
|
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."""
|
@@ -86,7 +86,7 @@ def get_users(*, limit: int, order_by: str = "id", offset: str | None = None) ->
|
|
86
86
|
return user_collection_schema.dump(UserCollection(users=users, total_entries=total_entries))
|
87
87
|
|
88
88
|
|
89
|
-
@requires_access_custom_view(
|
89
|
+
@requires_access_custom_view("POST", permissions.RESOURCE_USER)
|
90
90
|
def post_user() -> APIResponse:
|
91
91
|
"""Create a new user."""
|
92
92
|
try:
|
@@ -129,7 +129,7 @@ def post_user() -> APIResponse:
|
|
129
129
|
return user_schema.dump(user)
|
130
130
|
|
131
131
|
|
132
|
-
@requires_access_custom_view(
|
132
|
+
@requires_access_custom_view("PUT", permissions.RESOURCE_USER)
|
133
133
|
def patch_user(*, username: str, update_mask: UpdateMask = None) -> APIResponse:
|
134
134
|
"""Update a user."""
|
135
135
|
try:
|
@@ -198,7 +198,7 @@ def patch_user(*, username: str, update_mask: UpdateMask = None) -> APIResponse:
|
|
198
198
|
return user_schema.dump(user)
|
199
199
|
|
200
200
|
|
201
|
-
@requires_access_custom_view(
|
201
|
+
@requires_access_custom_view("DELETE", permissions.RESOURCE_USER)
|
202
202
|
def delete_user(*, username: str) -> APIResponse:
|
203
203
|
"""Delete a user."""
|
204
204
|
security_manager = cast(FabAirflowSecurityManagerOverride, get_auth_manager().security_manager)
|
@@ -93,11 +93,14 @@ def _has_access_fab(permissions: Sequence[tuple[str, str]] | None = None) -> Cal
|
|
93
93
|
|
94
94
|
if len(unique_dag_ids) > 1:
|
95
95
|
log.warning(
|
96
|
-
|
96
|
+
"There are different dag_ids passed in the request: %s. Returning 403.", unique_dag_ids
|
97
97
|
)
|
98
98
|
log.warning(
|
99
|
-
|
100
|
-
|
99
|
+
"kwargs: %s, args: %s, form: %s, json: %s",
|
100
|
+
dag_id_kwargs,
|
101
|
+
dag_id_args,
|
102
|
+
dag_id_form,
|
103
|
+
dag_id_json,
|
101
104
|
)
|
102
105
|
return (
|
103
106
|
render_template(
|
@@ -54,8 +54,6 @@ from airflow.providers.fab.auth_manager.cli_commands.definition import (
|
|
54
54
|
from airflow.providers.fab.auth_manager.models import Permission, Role, User
|
55
55
|
from airflow.security import permissions
|
56
56
|
from airflow.security.permissions import (
|
57
|
-
ACTION_CAN_ACCESS_MENU,
|
58
|
-
ACTION_CAN_READ,
|
59
57
|
RESOURCE_AUDIT_LOG,
|
60
58
|
RESOURCE_CLUSTER_ACTIVITY,
|
61
59
|
RESOURCE_CONFIG,
|
@@ -263,16 +261,19 @@ class FabAuthManager(BaseAuthManager):
|
|
263
261
|
return self._is_authorized(method=method, resource_type=RESOURCE_VARIABLE, user=user)
|
264
262
|
|
265
263
|
def is_authorized_view(self, *, access_view: AccessView, user: BaseUser | None = None) -> bool:
|
264
|
+
# "Docs" are only links in the menu, there is no page associated
|
265
|
+
method: ResourceMethod = "MENU" if access_view == AccessView.DOCS else "GET"
|
266
266
|
return self._is_authorized(
|
267
|
-
method=
|
267
|
+
method=method, resource_type=_MAP_ACCESS_VIEW_TO_FAB_RESOURCE_TYPE[access_view], user=user
|
268
268
|
)
|
269
269
|
|
270
270
|
def is_authorized_custom_view(
|
271
|
-
self, *,
|
271
|
+
self, *, method: ResourceMethod, resource_name: str, user: BaseUser | None = None
|
272
272
|
):
|
273
273
|
if not user:
|
274
274
|
user = self.get_user()
|
275
|
-
|
275
|
+
fab_action_name = get_fab_action_from_method_map()[method]
|
276
|
+
return (fab_action_name, resource_name) in self._get_user_permissions(user)
|
276
277
|
|
277
278
|
@provide_session
|
278
279
|
def get_permitted_dag_ids(
|
@@ -350,7 +351,7 @@ class FabAuthManager(BaseAuthManager):
|
|
350
351
|
if not self.security_manager.auth_view:
|
351
352
|
raise AirflowException("`auth_view` not defined in the security manager.")
|
352
353
|
if "next_url" in kwargs and kwargs["next_url"]:
|
353
|
-
return url_for(f"{self.security_manager.auth_view.endpoint}.login",
|
354
|
+
return url_for(f"{self.security_manager.auth_view.endpoint}.login", next=kwargs["next_url"])
|
354
355
|
else:
|
355
356
|
return url_for(f"{self.security_manager.auth_view.endpoint}.login")
|
356
357
|
|
@@ -463,18 +464,11 @@ class FabAuthManager(BaseAuthManager):
|
|
463
464
|
"""
|
464
465
|
Return the user permissions.
|
465
466
|
|
466
|
-
ACTION_CAN_READ and ACTION_CAN_ACCESS_MENU are merged into because they are very similar.
|
467
|
-
We can assume that if a user has permissions to read variables, they also have permissions to access
|
468
|
-
the menu "Variables".
|
469
|
-
|
470
467
|
:param user: the user to get permissions for
|
471
468
|
|
472
469
|
:meta private:
|
473
470
|
"""
|
474
|
-
|
475
|
-
return [
|
476
|
-
(ACTION_CAN_READ if perm[0] == ACTION_CAN_ACCESS_MENU else perm[0], perm[1]) for perm in perms
|
477
|
-
]
|
471
|
+
return getattr(user, "perms") or []
|
478
472
|
|
479
473
|
def _get_root_dag_id(self, dag_id: str) -> str:
|
480
474
|
"""
|
@@ -340,6 +340,43 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
340
340
|
# Setup Flask-Jwt-Extended
|
341
341
|
self.create_jwt_manager()
|
342
342
|
|
343
|
+
def _get_authentik_jwks(self, jwks_url) -> dict:
|
344
|
+
import requests
|
345
|
+
|
346
|
+
resp = requests.get(jwks_url)
|
347
|
+
if resp.status_code == 200:
|
348
|
+
return resp.json()
|
349
|
+
return {}
|
350
|
+
|
351
|
+
def _validate_jwt(self, id_token, jwks):
|
352
|
+
from authlib.jose import JsonWebKey, jwt as authlib_jwt
|
353
|
+
|
354
|
+
keyset = JsonWebKey.import_key_set(jwks)
|
355
|
+
claims = authlib_jwt.decode(id_token, keyset)
|
356
|
+
claims.validate()
|
357
|
+
log.info("JWT token is validated")
|
358
|
+
return claims
|
359
|
+
|
360
|
+
def _get_authentik_token_info(self, id_token):
|
361
|
+
me = jwt.decode(id_token, options={"verify_signature": False})
|
362
|
+
|
363
|
+
verify_signature = self.oauth_remotes["authentik"].client_kwargs.get("verify_signature", True)
|
364
|
+
if verify_signature:
|
365
|
+
# Validate the token using authentik certificate
|
366
|
+
jwks_uri = self.oauth_remotes["authentik"].server_metadata.get("jwks_uri")
|
367
|
+
if jwks_uri:
|
368
|
+
jwks = self._get_authentik_jwks(jwks_uri)
|
369
|
+
if jwks:
|
370
|
+
return self._validate_jwt(id_token, jwks)
|
371
|
+
else:
|
372
|
+
log.error("jwks_uri not specified in OAuth Providers, could not verify token signature")
|
373
|
+
else:
|
374
|
+
# Return the token info without validating
|
375
|
+
log.warning("JWT token is not validated!")
|
376
|
+
return me
|
377
|
+
|
378
|
+
raise AirflowException("OAuth signature verify failed")
|
379
|
+
|
343
380
|
def register_views(self):
|
344
381
|
"""Register FAB auth manager related views."""
|
345
382
|
if not self.appbuilder.get_app.config.get("FAB_ADD_SECURITY_VIEWS", True):
|
@@ -513,9 +550,10 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
513
550
|
def load_user_jwt(self, _jwt_header, jwt_data):
|
514
551
|
identity = jwt_data["sub"]
|
515
552
|
user = self.load_user(identity)
|
516
|
-
|
517
|
-
|
518
|
-
|
553
|
+
if user.is_active:
|
554
|
+
# Set flask g.user to JWT user, we can't do it on before request
|
555
|
+
g.user = user
|
556
|
+
return user
|
519
557
|
|
520
558
|
@property
|
521
559
|
def auth_type(self):
|
@@ -647,6 +685,10 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
647
685
|
"""The JMESPATH role to use for user registration."""
|
648
686
|
return self.appbuilder.get_app.config["AUTH_USER_REGISTRATION_ROLE_JMESPATH"]
|
649
687
|
|
688
|
+
@property
|
689
|
+
def auth_remote_user_env_var(self) -> str:
|
690
|
+
return self.appbuilder.get_app.config["AUTH_REMOTE_USER_ENV_VAR"]
|
691
|
+
|
650
692
|
@property
|
651
693
|
def api_login_allow_multiple_providers(self):
|
652
694
|
return self.appbuilder.get_app.config["AUTH_API_LOGIN_ALLOW_MULTIPLE_PROVIDERS"]
|
@@ -727,8 +769,8 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
727
769
|
# If the user does not exist, make a random password and make it
|
728
770
|
if not user_exists:
|
729
771
|
print(f"FlaskAppBuilder Authentication Manager: Creating {user_name} user")
|
730
|
-
role
|
731
|
-
|
772
|
+
if (role := self.find_role("Admin")) is None:
|
773
|
+
raise AirflowException("Unable to find role 'Admin'")
|
732
774
|
# password does not contain visually similar characters: ijlIJL1oO0
|
733
775
|
password = "".join(random.choices("abcdefghkmnpqrstuvwxyzABCDEFGHKMNPQRSTUVWXYZ23456789", k=16))
|
734
776
|
with open(password_path, "w") as file:
|
@@ -790,6 +832,9 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
790
832
|
app.config.setdefault("AUTH_LDAP_LASTNAME_FIELD", "sn")
|
791
833
|
app.config.setdefault("AUTH_LDAP_EMAIL_FIELD", "mail")
|
792
834
|
|
835
|
+
if self.auth_type == AUTH_REMOTE_USER:
|
836
|
+
app.config.setdefault("AUTH_REMOTE_USER_ENV_VAR", "REMOTE_USER")
|
837
|
+
|
793
838
|
# Rate limiting
|
794
839
|
app.config.setdefault("AUTH_RATE_LIMITED", True)
|
795
840
|
app.config.setdefault("AUTH_RATE_LIMIT", "5 per 40 second")
|
@@ -804,7 +849,12 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
804
849
|
if self.auth_type == AUTH_OID:
|
805
850
|
from flask_openid import OpenID
|
806
851
|
|
852
|
+
log.warning(
|
853
|
+
"AUTH_OID is deprecated and will be removed in version 5. "
|
854
|
+
"Migrate to other authentication methods."
|
855
|
+
)
|
807
856
|
self.oid = OpenID(app)
|
857
|
+
|
808
858
|
if self.auth_type == AUTH_OAUTH:
|
809
859
|
from authlib.integrations.flask_client import OAuth
|
810
860
|
|
@@ -1249,8 +1299,8 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
1249
1299
|
sesh = self.appbuilder.get_session
|
1250
1300
|
perms = sesh.query(Permission).filter(
|
1251
1301
|
or_(
|
1252
|
-
Permission.action == None, # noqa
|
1253
|
-
Permission.resource == None, # noqa
|
1302
|
+
Permission.action == None, # noqa: E711
|
1303
|
+
Permission.resource == None, # noqa: E711
|
1254
1304
|
)
|
1255
1305
|
)
|
1256
1306
|
# Since FAB doesn't define ON DELETE CASCADE on these tables, we need
|
@@ -1471,8 +1521,9 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
1471
1521
|
return False
|
1472
1522
|
|
1473
1523
|
def load_user(self, user_id):
|
1474
|
-
|
1475
|
-
|
1524
|
+
user = self.get_user_by_id(int(user_id))
|
1525
|
+
if user.is_active:
|
1526
|
+
return user
|
1476
1527
|
|
1477
1528
|
def get_user_by_id(self, pk):
|
1478
1529
|
return self.get_session.get(self.user_model, pk)
|
@@ -2208,6 +2259,19 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
2208
2259
|
"last_name": data.get("family_name", ""),
|
2209
2260
|
"email": data.get("email", ""),
|
2210
2261
|
}
|
2262
|
+
|
2263
|
+
# for Authentik
|
2264
|
+
if provider == "authentik":
|
2265
|
+
id_token = resp["id_token"]
|
2266
|
+
me = self._get_authentik_token_info(id_token)
|
2267
|
+
log.debug("User info from authentik: %s", me)
|
2268
|
+
return {
|
2269
|
+
"email": me["preferred_username"],
|
2270
|
+
"first_name": me.get("given_name", ""),
|
2271
|
+
"username": me["nickname"],
|
2272
|
+
"role_keys": me.get("groups", []),
|
2273
|
+
}
|
2274
|
+
|
2211
2275
|
else:
|
2212
2276
|
return {}
|
2213
2277
|
|
@@ -2445,8 +2509,9 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
2445
2509
|
:param ldap: The ldap module reference
|
2446
2510
|
:param con: The ldap connection
|
2447
2511
|
"""
|
2448
|
-
|
2449
|
-
|
2512
|
+
if not self.auth_ldap_bind_user:
|
2513
|
+
# always check AUTH_LDAP_BIND_USER is set before calling this method
|
2514
|
+
raise ValueError("AUTH_LDAP_BIND_USER must be set")
|
2450
2515
|
|
2451
2516
|
try:
|
2452
2517
|
log.debug("LDAP bind indirect TRY with username: %r", self.auth_ldap_bind_user)
|
@@ -2465,8 +2530,9 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
2465
2530
|
:param username: username to match with AUTH_LDAP_UID_FIELD
|
2466
2531
|
:return: ldap object array
|
2467
2532
|
"""
|
2468
|
-
|
2469
|
-
|
2533
|
+
if not self.auth_ldap_search:
|
2534
|
+
# always check AUTH_LDAP_SEARCH is set before calling this method
|
2535
|
+
raise ValueError("AUTH_LDAP_SEARCH must be set")
|
2470
2536
|
|
2471
2537
|
# build the filter string for the LDAP search
|
2472
2538
|
if self.auth_ldap_search_filter:
|
@@ -31,9 +31,9 @@ def get_provider_info():
|
|
31
31
|
"source-date-epoch": 1703288133,
|
32
32
|
"versions": ["1.0.2", "1.0.1", "1.0.0"],
|
33
33
|
"dependencies": [
|
34
|
-
"apache-airflow>=2.9.0
|
34
|
+
"apache-airflow>=2.9.0",
|
35
35
|
"flask>=2.2,<2.3",
|
36
|
-
"flask-appbuilder==4.
|
36
|
+
"flask-appbuilder==4.4.1",
|
37
37
|
"flask-login>=0.6.2",
|
38
38
|
"google-re2>=1.0",
|
39
39
|
],
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: apache-airflow-providers-fab
|
3
|
-
Version: 1.0.2.
|
3
|
+
Version: 1.0.2.dev2
|
4
4
|
Summary: Provider package apache-airflow-providers-fab for Apache Airflow
|
5
5
|
Keywords: airflow-provider,fab,airflow,integration
|
6
6
|
Author-email: Apache Software Foundation <dev@airflow.apache.org>
|
@@ -19,9 +19,10 @@ Classifier: Programming Language :: Python :: 3.8
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.9
|
20
20
|
Classifier: Programming Language :: Python :: 3.10
|
21
21
|
Classifier: Programming Language :: Python :: 3.11
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
22
23
|
Classifier: Topic :: System :: Monitoring
|
23
24
|
Requires-Dist: apache-airflow>=2.9.0.dev0
|
24
|
-
Requires-Dist: flask-appbuilder==4.
|
25
|
+
Requires-Dist: flask-appbuilder==4.4.1
|
25
26
|
Requires-Dist: flask-login>=0.6.2
|
26
27
|
Requires-Dist: flask>=2.2,<2.3
|
27
28
|
Requires-Dist: google-re2>=1.0
|
@@ -77,7 +78,7 @@ Project-URL: YouTube, https://www.youtube.com/channel/UCSXwxpWZQ7XZ1WL3wqevChA/
|
|
77
78
|
|
78
79
|
Package ``apache-airflow-providers-fab``
|
79
80
|
|
80
|
-
Release: ``1.0.2.
|
81
|
+
Release: ``1.0.2.dev2``
|
81
82
|
|
82
83
|
|
83
84
|
`Flask App Builder <https://flask-appbuilder.readthedocs.io/>`__
|
@@ -99,7 +100,7 @@ You can install this package on top of an existing Airflow 2 installation (see `
|
|
99
100
|
for the minimum Airflow version supported) via
|
100
101
|
``pip install apache-airflow-providers-fab``
|
101
102
|
|
102
|
-
The package supports the following python versions: 3.8,3.9,3.10,3.11
|
103
|
+
The package supports the following python versions: 3.8,3.9,3.10,3.11,3.12
|
103
104
|
|
104
105
|
Requirements
|
105
106
|
------------
|
@@ -107,9 +108,9 @@ Requirements
|
|
107
108
|
==================== ==================
|
108
109
|
PIP package Version required
|
109
110
|
==================== ==================
|
110
|
-
``apache-airflow`` ``>=2.9.0
|
111
|
+
``apache-airflow`` ``>=2.9.0``
|
111
112
|
``flask`` ``>=2.2,<2.3``
|
112
|
-
``flask-appbuilder`` ``==4.
|
113
|
+
``flask-appbuilder`` ``==4.4.1``
|
113
114
|
``flask-login`` ``>=0.6.2``
|
114
115
|
``google-re2`` ``>=1.0``
|
115
116
|
==================== ==================
|
@@ -1,38 +1,38 @@
|
|
1
1
|
airflow/providers/fab/LICENSE,sha256=ywUBpKZc7Jb96rVt5I3IDbg7dIJAbUSHkuoDcF3jbH4,13569
|
2
2
|
airflow/providers/fab/__init__.py,sha256=i7O17EJB9DcN_BQKB75NJcb0CEYr7Y3gzfZNm2Fpx9s,1578
|
3
|
-
airflow/providers/fab/get_provider_info.py,sha256=
|
3
|
+
airflow/providers/fab/get_provider_info.py,sha256=cojfnZKpmibjoYUrcf_Ab4fMZsm16dscFwS5tqU8fNw,2944
|
4
4
|
airflow/providers/fab/auth_manager/__init__.py,sha256=mlJxuZLkd5x-iq2SBwD3mvRQpt3YR7wjz_nceyF1IaI,787
|
5
|
-
airflow/providers/fab/auth_manager/fab_auth_manager.py,sha256=
|
5
|
+
airflow/providers/fab/auth_manager/fab_auth_manager.py,sha256=det_jvKb0hozwC7XgLgJXjYbdnW58zrtA1k0RhOUWbA,19545
|
6
6
|
airflow/providers/fab/auth_manager/api/__init__.py,sha256=mlJxuZLkd5x-iq2SBwD3mvRQpt3YR7wjz_nceyF1IaI,787
|
7
7
|
airflow/providers/fab/auth_manager/api/auth/__init__.py,sha256=mlJxuZLkd5x-iq2SBwD3mvRQpt3YR7wjz_nceyF1IaI,787
|
8
8
|
airflow/providers/fab/auth_manager/api/auth/backend/__init__.py,sha256=mlJxuZLkd5x-iq2SBwD3mvRQpt3YR7wjz_nceyF1IaI,787
|
9
|
-
airflow/providers/fab/auth_manager/api/auth/backend/basic_auth.py,sha256=
|
9
|
+
airflow/providers/fab/auth_manager/api/auth/backend/basic_auth.py,sha256=UBGP5UHV6kNsChvL9rkYOQWHszEJtE4AkSFagcRsqrs,2502
|
10
10
|
airflow/providers/fab/auth_manager/api/auth/backend/kerberos_auth.py,sha256=SvuJ9jo06ZfB_40FUKaiHb8cw2d3Jpxm-YYSOWEzA7I,1712
|
11
11
|
airflow/providers/fab/auth_manager/api_endpoints/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
|
12
|
-
airflow/providers/fab/auth_manager/api_endpoints/role_and_permission_endpoint.py,sha256=
|
13
|
-
airflow/providers/fab/auth_manager/api_endpoints/user_endpoint.py,sha256=
|
12
|
+
airflow/providers/fab/auth_manager/api_endpoints/role_and_permission_endpoint.py,sha256=9GRizOLGF5Rsic_42H_gom8LkjVTpvJT0oRxO98lwlw,7556
|
13
|
+
airflow/providers/fab/auth_manager/api_endpoints/user_endpoint.py,sha256=8GfpmpkABVO834Os8rNoH_rqLh4eFz1sfayL1Fwz8FQ,8488
|
14
14
|
airflow/providers/fab/auth_manager/cli_commands/__init__.py,sha256=mlJxuZLkd5x-iq2SBwD3mvRQpt3YR7wjz_nceyF1IaI,787
|
15
15
|
airflow/providers/fab/auth_manager/cli_commands/definition.py,sha256=hjcb3IL-G1s120UcdOZcV5ssgC7uuTfTmHECGlmr7bk,9075
|
16
|
-
airflow/providers/fab/auth_manager/cli_commands/role_command.py,sha256=
|
17
|
-
airflow/providers/fab/auth_manager/cli_commands/sync_perm_command.py,sha256=
|
18
|
-
airflow/providers/fab/auth_manager/cli_commands/user_command.py,sha256=
|
16
|
+
airflow/providers/fab/auth_manager/cli_commands/role_command.py,sha256=4w1tHTR5gBbsymeqGIJ4Rs8CmGdw0l49y58pfI0DB_s,9098
|
17
|
+
airflow/providers/fab/auth_manager/cli_commands/sync_perm_command.py,sha256=VpW-rWhgHAL_ReU66D_BrsxlXQN4okfxzj6dyE5IfwA,1663
|
18
|
+
airflow/providers/fab/auth_manager/cli_commands/user_command.py,sha256=qaYuBbIvF_P0C_SIdQuEFS-hcjRkyn74Gl9Jt0hh2Gs,10207
|
19
19
|
airflow/providers/fab/auth_manager/cli_commands/utils.py,sha256=V--SdqJgAtNkxmqhOq4Jvpiqk5Dec7OCbIAHih6RPYM,1768
|
20
20
|
airflow/providers/fab/auth_manager/decorators/__init__.py,sha256=mlJxuZLkd5x-iq2SBwD3mvRQpt3YR7wjz_nceyF1IaI,787
|
21
|
-
airflow/providers/fab/auth_manager/decorators/auth.py,sha256=
|
21
|
+
airflow/providers/fab/auth_manager/decorators/auth.py,sha256=Z4_xiS1N1E0MqDP9OxHx_YJvKe0sH15afP0tn5LP3Ss,4948
|
22
22
|
airflow/providers/fab/auth_manager/models/__init__.py,sha256=IEGXoz1HaCHxoH8DzI5DIoYHBKlrN1wjru7Ey_YhtkA,8827
|
23
23
|
airflow/providers/fab/auth_manager/models/anonymous_user.py,sha256=ki1jM-SdxSEJzXhrsiulmziWHlKkU2LguQkXMaRmykE,1775
|
24
24
|
airflow/providers/fab/auth_manager/openapi/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
|
25
25
|
airflow/providers/fab/auth_manager/openapi/v1.yaml,sha256=xFlQMccLoGarx8SnQvGJ1DRfHo4jQ3p9DzoPqQINcCw,19380
|
26
26
|
airflow/providers/fab/auth_manager/security_manager/__init__.py,sha256=mlJxuZLkd5x-iq2SBwD3mvRQpt3YR7wjz_nceyF1IaI,787
|
27
27
|
airflow/providers/fab/auth_manager/security_manager/constants.py,sha256=x1Sjl_Mu3wmaSy3NFZlHxK2z-juzWmMs1SrzJ0aiBBQ,907
|
28
|
-
airflow/providers/fab/auth_manager/security_manager/override.py,sha256=
|
28
|
+
airflow/providers/fab/auth_manager/security_manager/override.py,sha256=OMJca7teRJ4ria11J-uAIvipKri7gm-YyG1d9i_MyME,111419
|
29
29
|
airflow/providers/fab/auth_manager/views/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
|
30
30
|
airflow/providers/fab/auth_manager/views/permissions.py,sha256=k5APUTqqKZqMxCWlKrUEgqdDVrGa9719HJ3UmFpiSLc,2886
|
31
31
|
airflow/providers/fab/auth_manager/views/roles_list.py,sha256=o_VqnLbQa4sisKxLwyx6VCl3HmvjKcjkrJG23R81FMQ,1494
|
32
32
|
airflow/providers/fab/auth_manager/views/user.py,sha256=XsUXXx4qEbZJYMTmWz3R-lpavsUZXvpwKAASHjxceGc,5917
|
33
33
|
airflow/providers/fab/auth_manager/views/user_edit.py,sha256=d8wQ_8Rk8Ce95sCElZIXN7ATwbXrT2Tauqn5uTwuo2E,2140
|
34
34
|
airflow/providers/fab/auth_manager/views/user_stats.py,sha256=zP2eX6e40rpEohJcvnvri4Khu3Q4ULLxjz1zOWvOr8A,1300
|
35
|
-
apache_airflow_providers_fab-1.0.2.
|
36
|
-
apache_airflow_providers_fab-1.0.2.
|
37
|
-
apache_airflow_providers_fab-1.0.2.
|
38
|
-
apache_airflow_providers_fab-1.0.2.
|
35
|
+
apache_airflow_providers_fab-1.0.2.dev2.dist-info/entry_points.txt,sha256=m05kASp7vFi0ZmQ--CFp7GeJpPL7UT2RQF8EEP5XRX8,99
|
36
|
+
apache_airflow_providers_fab-1.0.2.dev2.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
|
37
|
+
apache_airflow_providers_fab-1.0.2.dev2.dist-info/METADATA,sha256=vG2rgmc1Bf1u2knabEyTHwdsjXrRnDG-UH5vMSm0AkY,4962
|
38
|
+
apache_airflow_providers_fab-1.0.2.dev2.dist-info/RECORD,,
|
File without changes
|