apache-airflow-providers-fab 2.3.0rc1__py3-none-any.whl → 2.3.1rc1__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/__init__.py +1 -1
- airflow/providers/fab/auth_manager/fab_auth_manager.py +9 -3
- airflow/providers/fab/auth_manager/security_manager/override.py +5 -2
- airflow/providers/fab/www/auth.py +18 -35
- airflow/providers/fab/www/extensions/init_jinja_globals.py +5 -2
- airflow/providers/fab/www/package-lock.json +199 -219
- airflow/providers/fab/www/package.json +7 -7
- airflow/providers/fab/www/utils.py +7 -2
- {apache_airflow_providers_fab-2.3.0rc1.dist-info → apache_airflow_providers_fab-2.3.1rc1.dist-info}/METADATA +32 -31
- {apache_airflow_providers_fab-2.3.0rc1.dist-info → apache_airflow_providers_fab-2.3.1rc1.dist-info}/RECORD +14 -14
- {apache_airflow_providers_fab-2.3.0rc1.dist-info → apache_airflow_providers_fab-2.3.1rc1.dist-info}/WHEEL +0 -0
- {apache_airflow_providers_fab-2.3.0rc1.dist-info → apache_airflow_providers_fab-2.3.1rc1.dist-info}/entry_points.txt +0 -0
- {apache_airflow_providers_fab-2.3.0rc1.dist-info → apache_airflow_providers_fab-2.3.1rc1.dist-info}/licenses/3rd-party-licenses/LICENSES-ui.txt +0 -0
- {apache_airflow_providers_fab-2.3.0rc1.dist-info → apache_airflow_providers_fab-2.3.1rc1.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.3.
|
32
|
+
__version__ = "2.3.1"
|
33
33
|
|
34
34
|
if packaging.version.parse(packaging.version.parse(airflow_version).base_version) < packaging.version.parse(
|
35
35
|
"3.0.2"
|
@@ -34,6 +34,12 @@ from starlette.middleware.wsgi import WSGIMiddleware
|
|
34
34
|
from airflow import __version__ as airflow_version
|
35
35
|
from airflow.api_fastapi.app import AUTH_MANAGER_FASTAPI_APP_PREFIX
|
36
36
|
from airflow.api_fastapi.auth.managers.base_auth_manager import BaseAuthManager
|
37
|
+
|
38
|
+
try:
|
39
|
+
from airflow.api_fastapi.auth.managers.base_auth_manager import ExtendedResourceMethod
|
40
|
+
except ImportError:
|
41
|
+
from airflow.api_fastapi.auth.managers.base_auth_manager import ResourceMethod as ExtendedResourceMethod
|
42
|
+
|
37
43
|
from airflow.api_fastapi.auth.managers.models.resource_details import (
|
38
44
|
AccessView,
|
39
45
|
BackfillDetails,
|
@@ -382,7 +388,7 @@ class FabAuthManager(BaseAuthManager[User]):
|
|
382
388
|
|
383
389
|
def is_authorized_view(self, *, access_view: AccessView, user: User) -> bool:
|
384
390
|
# "Docs" are only links in the menu, there is no page associated
|
385
|
-
method:
|
391
|
+
method: ExtendedResourceMethod = "MENU" if access_view == AccessView.DOCS else "GET"
|
386
392
|
return self._is_authorized(
|
387
393
|
method=method,
|
388
394
|
resource_type=_MAP_ACCESS_VIEW_TO_FAB_RESOURCE_TYPE[access_view],
|
@@ -523,7 +529,7 @@ class FabAuthManager(BaseAuthManager[User]):
|
|
523
529
|
def _is_authorized(
|
524
530
|
self,
|
525
531
|
*,
|
526
|
-
method:
|
532
|
+
method: ExtendedResourceMethod,
|
527
533
|
resource_type: str,
|
528
534
|
user: User,
|
529
535
|
) -> bool:
|
@@ -594,7 +600,7 @@ class FabAuthManager(BaseAuthManager[User]):
|
|
594
600
|
return len(authorized_dags) > 0
|
595
601
|
|
596
602
|
@staticmethod
|
597
|
-
def _get_fab_action(method:
|
603
|
+
def _get_fab_action(method: ExtendedResourceMethod) -> str:
|
598
604
|
"""
|
599
605
|
Convert the method to a FAB action.
|
600
606
|
|
@@ -1551,7 +1551,7 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
1551
1551
|
---------------
|
1552
1552
|
"""
|
1553
1553
|
|
1554
|
-
def get_resource(self, name: str) -> Resource:
|
1554
|
+
def get_resource(self, name: str) -> Resource | None:
|
1555
1555
|
"""
|
1556
1556
|
Return a resource record by name, if it exists.
|
1557
1557
|
|
@@ -1559,7 +1559,7 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
1559
1559
|
"""
|
1560
1560
|
return self.get_session.query(self.resource_model).filter_by(name=name).one_or_none()
|
1561
1561
|
|
1562
|
-
def create_resource(self, name) -> Resource:
|
1562
|
+
def create_resource(self, name) -> Resource | None:
|
1563
1563
|
"""
|
1564
1564
|
Create a resource with the given name.
|
1565
1565
|
|
@@ -1628,6 +1628,9 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
|
|
1628
1628
|
if perm:
|
1629
1629
|
return perm
|
1630
1630
|
resource = self.create_resource(resource_name)
|
1631
|
+
if resource is None:
|
1632
|
+
log.error(const.LOGMSG_ERR_SEC_ADD_PERMVIEW, f"Resource creation failed {resource_name}")
|
1633
|
+
return None
|
1631
1634
|
action = self.create_action(action_name)
|
1632
1635
|
perm = self.permission_model()
|
1633
1636
|
perm.resource_id, perm.action_id = resource.id, action.id
|
@@ -46,10 +46,7 @@ from airflow.utils.net import get_hostname
|
|
46
46
|
if TYPE_CHECKING:
|
47
47
|
from airflow.api_fastapi.auth.managers.base_auth_manager import ResourceMethod
|
48
48
|
from airflow.api_fastapi.auth.managers.models.batch_apis import (
|
49
|
-
IsAuthorizedConnectionRequest,
|
50
49
|
IsAuthorizedDagRequest,
|
51
|
-
IsAuthorizedPoolRequest,
|
52
|
-
IsAuthorizedVariableRequest,
|
53
50
|
)
|
54
51
|
from airflow.models import DagRun, Pool, TaskInstance, Variable
|
55
52
|
from airflow.models.connection import Connection
|
@@ -170,15 +167,13 @@ def has_access_connection(method: ResourceMethod) -> Callable[[T], T]:
|
|
170
167
|
@wraps(func)
|
171
168
|
def decorated(*args, **kwargs):
|
172
169
|
connections: set[Connection] = set(args[1])
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
170
|
+
is_authorized = all(
|
171
|
+
get_auth_manager().is_authorized_connection(
|
172
|
+
method=method,
|
173
|
+
details=ConnectionDetails(conn_id=connection.conn_id),
|
174
|
+
user=get_auth_manager().get_user(),
|
175
|
+
)
|
178
176
|
for connection in connections
|
179
|
-
]
|
180
|
-
is_authorized = get_auth_manager().batch_is_authorized_connection(
|
181
|
-
requests, user=get_auth_manager().get_user()
|
182
177
|
)
|
183
178
|
return _has_access(
|
184
179
|
is_authorized=is_authorized,
|
@@ -284,15 +279,11 @@ def has_access_pool(method: ResourceMethod) -> Callable[[T], T]:
|
|
284
279
|
@wraps(func)
|
285
280
|
def decorated(*args, **kwargs):
|
286
281
|
pools: set[Pool] = set(args[1])
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
}
|
282
|
+
is_authorized = all(
|
283
|
+
get_auth_manager().is_authorized_pool(
|
284
|
+
method=method, details=PoolDetails(name=pool.pool), user=get_auth_manager().get_user()
|
285
|
+
)
|
292
286
|
for pool in pools
|
293
|
-
]
|
294
|
-
is_authorized = get_auth_manager().batch_is_authorized_pool(
|
295
|
-
requests, user=get_auth_manager().get_user()
|
296
287
|
)
|
297
288
|
return _has_access(
|
298
289
|
is_authorized=is_authorized,
|
@@ -310,23 +301,15 @@ def has_access_variable(method: ResourceMethod) -> Callable[[T], T]:
|
|
310
301
|
def has_access_decorator(func: T):
|
311
302
|
@wraps(func)
|
312
303
|
def decorated(*args, **kwargs):
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
method=method,
|
317
|
-
|
318
|
-
|
319
|
-
variables: set[Variable] = set(args[1])
|
320
|
-
requests: Sequence[IsAuthorizedVariableRequest] = [
|
321
|
-
{
|
322
|
-
"method": method,
|
323
|
-
"details": VariableDetails(key=variable.key),
|
324
|
-
}
|
325
|
-
for variable in variables
|
326
|
-
]
|
327
|
-
is_authorized = get_auth_manager().batch_is_authorized_variable(
|
328
|
-
requests, user=get_auth_manager().get_user()
|
304
|
+
variables: set[Variable] = set(args[1])
|
305
|
+
is_authorized = all(
|
306
|
+
get_auth_manager().is_authorized_variable(
|
307
|
+
method=method,
|
308
|
+
details=VariableDetails(key=variable.key),
|
309
|
+
user=get_auth_manager().get_user(),
|
329
310
|
)
|
311
|
+
for variable in variables
|
312
|
+
)
|
330
313
|
return _has_access(
|
331
314
|
is_authorized=is_authorized,
|
332
315
|
func=func,
|
@@ -18,7 +18,7 @@ from __future__ import annotations
|
|
18
18
|
|
19
19
|
import logging
|
20
20
|
|
21
|
-
import
|
21
|
+
from pendulum import local_timezone
|
22
22
|
|
23
23
|
import airflow
|
24
24
|
from airflow.api_fastapi.app import get_auth_manager
|
@@ -33,7 +33,10 @@ def init_jinja_globals(app, enable_plugins: bool):
|
|
33
33
|
"""Add extra globals variable to Jinja context."""
|
34
34
|
server_timezone = conf.get("core", "default_timezone")
|
35
35
|
if server_timezone == "system":
|
36
|
-
|
36
|
+
if callable(local_timezone):
|
37
|
+
server_timezone = local_timezone().name
|
38
|
+
else:
|
39
|
+
raise ValueError("`local_timezone` is not callable")
|
37
40
|
elif server_timezone == "utc":
|
38
41
|
server_timezone = "UTC"
|
39
42
|
|