Repository: apache/airflow
Pull request #47241: AIP 84 - Add auth for asset alias
Author: login: rawwar
Labels: name: area:API
description: Airflow's REST/HTTP API
name: area:UI
description: Related to UI/UX. For Frontend Developers.
name: AIP-84
description: Modern Rest API
Created: 2025-03-01T11:38:40Z | Merged: 2025-03-10T10:06:04Z

related to #42360


Diff:
diff --git a/airflow/api_fastapi/auth/managers/base_auth_manager.py b/airflow/api_fastapi/auth/managers/base_auth_manager.py
index 6d2e2eb3f4c2c..30978e7c518c5 100644
--- a/airflow/api_fastapi/auth/managers/base_auth_manager.py
+++ b/airflow/api_fastapi/auth/managers/base_auth_manager.py
@@ -47,6 +47,7 @@
     )
     from airflow.api_fastapi.auth.managers.models.resource_details import (
         AccessView,
+        AssetAliasDetails,
         AssetDetails,
         ConfigurationDetails,
         ConnectionDetails,
@@ -174,6 +175,22 @@ def is_authorized_asset(
         :param details: optional details about the asset
         """
 
+    @abstractmethod
+    def is_authorized_asset_alias(
+        self,
+        *,
+        method: ResourceMethod,
+        user: T,
+        details: AssetAliasDetails | None = None,
+    ) -> bool:
+        """
+        Return whether the user is authorized to perform a given action on an asset alias.
+
+        :param method: the method to perform
+        :param user: the user to perform the action on
+        :param details: optional details about the asset alias
+        """
+
     @abstractmethod
     def is_authorized_pool(
         self,
diff --git a/airflow/api_fastapi/auth/managers/models/resource_details.py b/airflow/api_fastapi/auth/managers/models/resource_details.py
index 39d4a8a1450c2..2b4e95ef4b052 100644
--- a/airflow/api_fastapi/auth/managers/models/resource_details.py
+++ b/airflow/api_fastapi/auth/managers/models/resource_details.py
@@ -49,6 +49,13 @@ class AssetDetails:
     id: str | None = None
 
 
+@dataclass
+class AssetAliasDetails:
+    """Represents the details of an asset alias."""
+
+    id: str | None = None
+
+
 @dataclass
 class PoolDetails:
     """Represents the details of a pool."""
diff --git a/airflow/api_fastapi/auth/managers/simple/simple_auth_manager.py b/airflow/api_fastapi/auth/managers/simple/simple_auth_manager.py
index 58f65c9cf22dc..bb42aa7aef445 100644
--- a/airflow/api_fastapi/auth/managers/simple/simple_auth_manager.py
+++ b/airflow/api_fastapi/auth/managers/simple/simple_auth_manager.py
@@ -40,6 +40,7 @@
     from airflow.api_fastapi.auth.managers.base_auth_manager import ResourceMethod
     from airflow.api_fastapi.auth.managers.models.resource_details import (
         AccessView,
+        AssetAliasDetails,
         AssetDetails,
         ConfigurationDetails,
         ConnectionDetails,
@@ -180,6 +181,20 @@ def is_authorized_asset(
             user=user,
         )
 
+    def is_authorized_asset_alias(
+        self,
+        *,
+        method: ResourceMethod,
+        user: SimpleAuthManagerUser,
+        details: AssetAliasDetails | None = None,
+    ) -> bool:
+        return self._is_authorized(
+            method=method,
+            allow_get_role=SimpleAuthManagerRole.VIEWER,
+            allow_role=SimpleAuthManagerRole.OP,
+            user=user,
+        )
+
     def is_authorized_pool(
         self,
         *,
diff --git a/airflow/api_fastapi/core_api/openapi/v1-generated.yaml b/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
index ac9079c251d50..740f17d543737 100644
--- a/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
+++ b/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
@@ -616,6 +616,8 @@ paths:
       summary: Get Asset Aliases
       description: Get asset aliases.
       operationId: get_asset_aliases
+      security:
+      - OAuth2PasswordBearer: []
       parameters:
       - name: limit
         in: query
@@ -686,6 +688,8 @@ paths:
       summary: Get Asset Alias
       description: Get an asset alias.
       operationId: get_asset_alias
+      security:
+      - OAuth2PasswordBearer: []
       parameters:
       - name: asset_alias_id
         in: path
diff --git a/airflow/api_fastapi/core_api/routes/public/assets.py b/airflow/api_fastapi/core_api/routes/public/assets.py
index 36f4042c753bc..b37111e1f8da0 100644
--- a/airflow/api_fastapi/core_api/routes/public/assets.py
+++ b/airflow/api_fastapi/core_api/routes/public/assets.py
@@ -53,7 +53,11 @@
 )
 from airflow.api_fastapi.core_api.datamodels.dag_run import DAGRunResponse
 from airflow.api_fastapi.core_api.openapi.exceptions import create_openapi_http_exception_doc
-from airflow.api_fastapi.core_api.security import requires_access_asset, requires_access_dag
+from airflow.api_fastapi.core_api.security import (
+    requires_access_asset,
+    requires_access_asset_alias,
+    requires_access_dag,
+)
 from airflow.api_fastapi.logging.decorators import action_logging
 from airflow.assets.manager import asset_manager
 from airflow.models.asset import (
@@ -130,6 +134,7 @@ def get_assets(
 @assets_router.get(
     "/assets/aliases",
     responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]),
+    dependencies=[Depends(requires_access_asset_alias(method="GET"))],
 )
 def get_asset_aliases(
     limit: QueryLimit,
@@ -160,6 +165,7 @@ def get_asset_aliases(
 @assets_router.get(
     "/assets/aliases/{asset_alias_id}",
     responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]),
+    dependencies=[Depends(requires_access_asset_alias(method="GET"))],
 )
 def get_asset_alias(asset_alias_id: int, session: SessionDep):
     """Get an asset alias."""
diff --git a/airflow/api_fastapi/core_api/security.py b/airflow/api_fastapi/core_api/security.py
index 54c7c728919d4..ba7a7e2310451 100644
--- a/airflow/api_fastapi/core_api/security.py
+++ b/airflow/api_fastapi/core_api/security.py
@@ -26,6 +26,7 @@
 from airflow.api_fastapi.app import get_auth_manager
 from airflow.api_fastapi.auth.managers.models.base_user import BaseUser
 from airflow.api_fastapi.auth.managers.models.resource_details import (
+    AssetAliasDetails,
     AssetDetails,
     ConfigurationDetails,
     ConnectionDetails,
@@ -174,6 +175,22 @@ def inner(
     return inner
 
 
+def requires_access_asset_alias(method: ResourceMethod) -> Callable:
+    def inner(
+        request: Request,
+        user: Annotated[BaseUser | None, Depends(get_user)] = None,
+    ) -> None:
+        asset_alias_id: str | None = request.path_params.get("asset_alias_id")
+
+        _requires_access(
+            is_authorized_callback=lambda: get_auth_manager().is_authorized_asset_alias(
+                method=method, details=AssetAliasDetails(id=asset_alias_id), user=user
+            ),
+        )
+
+    return inner
+
+
 def _requires_access(
     *,
     is_authorized_callback: Callable[[], bool],
diff --git a/airflow/security/permissions.py b/airflow/security/permissions.py
index acd245865a4ad..33f0c28bc9a6b 100644
--- a/airflow/security/permissions.py
+++ b/airflow/security/permissions.py
@@ -34,6 +34,7 @@
 RESOURCE_DAG_WARNING = "DAG Warnings"
 RESOURCE_CLUSTER_ACTIVITY = "Cluster Activity"
 RESOURCE_ASSET = "Assets"
+RESOURCE_ASSET_ALIAS = "Asset Aliases"
 RESOURCE_DOCS = "Documentation"
 RESOURCE_DOCS_MENU = "Docs"
 RESOURCE_IMPORT_ERROR = "ImportError"
diff --git a/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/avp/entities.py b/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/avp/entities.py
index 54a43c5157203..e93f4af5855fe 100644
--- a/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/avp/entities.py
+++ b/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/avp/entities.py
@@ -34,6 +34,7 @@ class AvpEntities(Enum):
 
     # Resource types
     ASSET = "Asset"
+    ASSET_ALIAS = "AssetAlias"
     CONFIGURATION = "Configuration"
     CONNECTION = "Connection"
     CUSTOM = "Custom"
diff --git a/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py b/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py
index 2632422a1cba4..0c0c236e0b1af 100644
--- a/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py
+++ b/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py
@@ -55,7 +55,11 @@
         IsAuthorizedPoolRequest,
         IsAuthorizedVariableRequest,
     )
-    from airflow.api_fastapi.auth.managers.models.resource_details import AssetDetails, ConfigurationDetails
+    from airflow.api_fastapi.auth.managers.models.resource_details import (
+        AssetAliasDetails,
+        AssetDetails,
+        ConfigurationDetails,
+    )
 
 
 class AwsAuthManager(BaseAuthManager[AwsAuthManagerUser]):
@@ -158,6 +162,14 @@ def is_authorized_asset(
             method=method, entity_type=AvpEntities.ASSET, user=user, entity_id=asset_id
         )
 
+    def is_authorized_asset_alias(
+        self, *, method: ResourceMethod, user: AwsAuthManagerUser, details: AssetAliasDetails | None = None
+    ) -> bool:
+        asset_alias_id = details.id if details else None
+        return self.avp_facade.is_authorized(
+            method=method, entity_type=AvpEntities.ASSET_ALIAS, user=user, entity_id=asset_alias_id
+        )
+
     def is_authorized_pool(
         self, *, method: ResourceMethod, user: AwsAuthManagerUser, details: PoolDetails | None = None
     ) -> bool:
diff --git a/providers/amazon/tests/unit/amazon/aws/auth_manager/test_aws_auth_manager.py b/providers/amazon/tests/unit/amazon/aws/auth_manager/test_aws_auth_manager.py
index 328114311762a..a1e0c3d62cffc 100644
--- a/providers/amazon/tests/unit/amazon/aws/auth_manager/test_aws_auth_manager.py
+++ b/providers/amazon/tests/unit/amazon/aws/auth_manager/test_aws_auth_manager.py
@@ -43,9 +43,9 @@
 
 if TYPE_CHECKING:
     from airflow.api_fastapi.auth.managers.base_auth_manager import ResourceMethod
-    from airflow.api_fastapi.auth.managers.models.resource_details import AssetDetails
+    from airflow.api_fastapi.auth.managers.models.resource_details import AssetAliasDetails, AssetDetails
 else:
-    from airflow.providers.common.compat.assets import AssetDetails
+    from airflow.providers.common.compat.assets import AssetAliasDetails, AssetDetails
 
 
 mock = Mock()
@@ -229,6 +229,37 @@ def test_is_authorized_asset(
         )
         assert result
 
+    @pytest.mark.parametrize(
+        "details, user, expected_user, expected_entity_id",
+        [
+            (None, mock, ANY, None),
+            (AssetAliasDetails(id="1"), mock, mock, "1"),
+        ],
+    )
+    @patch.object(AwsAuthManager, "avp_facade")
+    def test_is_authorized_asset_alias(
+        self,
+        mock_avp_facade,
+        details,
+        user,
+        expected_user,
+        expected_entity_id,
+        auth_manager,
+    ):
+        is_authorized = Mock(return_value=True)
+        mock_avp_facade.is_authorized = is_authorized
+
+        method: ResourceMethod = "GET"
+        result = auth_manager.is_authorized_asset_alias(method=method, details=details, user=user)
+
+        is_authorized.assert_called_once_with(
+            method=method,
+            entity_type=AvpEntities.ASSET_ALIAS,
+            user=expected_user,
+            entity_id=expected_entity_id,
+        )
+        assert result
+
     @pytest.mark.parametrize(
         "details, user, expected_user, expected_entity_id",
         [
diff --git a/providers/common/compat/src/airflow/providers/common/compat/assets/__init__.py b/providers/common/compat/src/airflow/providers/common/compat/assets/__init__.py
index 01e218d920842..01fb1ab201828 100644
--- a/providers/common/compat/src/airflow/providers/common/compat/assets/__init__.py
+++ b/providers/common/compat/src/airflow/providers/common/compat/assets/__init__.py
@@ -25,12 +25,12 @@
 )
 
 if TYPE_CHECKING:
-    from airflow.api_fastapi.auth.managers.models.resource_details import AssetDetails
+    from airflow.api_fastapi.auth.managers.models.resource_details import AssetAliasDetails, AssetDetails
     from airflow.models.asset import expand_alias_to_assets
     from airflow.sdk.definitions.asset import Asset, AssetAlias, AssetAll, AssetAny
 else:
     if AIRFLOW_V_3_0_PLUS:
-        from airflow.api_fastapi.auth.managers.models.resource_details import AssetDetails
+        from airflow.api_fastapi.auth.managers.models.resource_details import AssetAliasDetails, AssetDetails
         from airflow.models.asset import expand_alias_to_assets
         from airflow.sdk.definitions.asset import Asset, AssetAlias, AssetAll, AssetAny
     else:
@@ -52,6 +52,7 @@
 __all__ = [
     "Asset",
     "AssetAlias",
+    "AssetAliasDetails",
     "AssetAll",
     "AssetAny",
     "AssetDetails",
diff --git a/providers/common/compat/src/airflow/providers/common/compat/security/permissions.py b/providers/common/compat/src/airflow/providers/common/compat/security/permissions.py
index d5c351bdad31e..46525c0ca3c29 100644
--- a/providers/common/compat/src/airflow/providers/common/compat/security/permissions.py
+++ b/providers/common/compat/src/airflow/providers/common/compat/security/permissions.py
@@ -19,12 +19,12 @@
 from typing import TYPE_CHECKING
 
 if TYPE_CHECKING:
-    from airflow.security.permissions import RESOURCE_ASSET
+    from airflow.security.permissions import RESOURCE_ASSET, RESOURCE_ASSET_ALIAS
 else:
     try:
-        from airflow.security.permissions import RESOURCE_ASSET
+        from airflow.security.permissions import RESOURCE_ASSET, RESOURCE_ASSET_ALIAS
     except ImportError:
         from airflow.security.permissions import RESOURCE_DATASET as RESOURCE_ASSET
 
 
-__all__ = ["RESOURCE_ASSET"]
+__all__ = ["RESOURCE_ASSET", "RESOURCE_ASSET_ALIAS"]
diff --git a/providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py b/providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py
index e881815569aff..0c721e713a199 100644
--- a/providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py
+++ b/providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py
@@ -97,12 +97,12 @@
     from airflow.cli.cli_config import (
         CLICommand,
     )
-    from airflow.providers.common.compat.assets import AssetDetails
+    from airflow.providers.common.compat.assets import AssetAliasDetails, AssetDetails
     from airflow.providers.fab.auth_manager.security_manager.override import FabAirflowSecurityManagerOverride
     from airflow.providers.fab.www.extensions.init_appbuilder import AirflowAppBuilder
-    from airflow.providers.fab.www.security.permissions import RESOURCE_ASSET
+    from airflow.providers.fab.www.security.permissions import RESOURCE_ASSET, RESOURCE_ASSET_ALIAS
 else:
-    from airflow.providers.common.compat.security.permissions import RESOURCE_ASSET
+    from airflow.providers.common.compat.security.permissions import RESOURCE_ASSET, RESOURCE_ASSET_ALIAS
 
 
 _MAP_DAG_ACCESS_ENTITY_TO_FAB_RESOURCE_TYPE: dict[DagAccessEntity, tuple[str, ...]] = {
@@ -319,6 +319,11 @@ def is_authorized_asset(
     ) -> bool:
         return self._is_authorized(method=method, resource_type=RESOURCE_ASSET, user=user)
 
+    def is_authorized_asset_alias(
+        self, *, method: ResourceMethod, user: User, details: AssetAliasDetails | None = None
+    ) -> bool:
+        return self._is_authorized(method=method, resource_type=RESOURCE_ASSET_ALIAS, user=user)
+
     def is_authorized_pool(
         self, *, method: ResourceMethod, user: User, details: PoolDetails | None = None
     ) -> bool:
diff --git a/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py b/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
index 4e83dc870cda9..26a3e08e880e2 100644
--- a/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
+++ b/providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
@@ -112,9 +112,9 @@
 )
 
 if TYPE_CHECKING:
-    from airflow.providers.fab.www.security.permissions import RESOURCE_ASSET
+    from airflow.providers.fab.www.security.permissions import RESOURCE_ASSET, RESOURCE_ASSET_ALIAS
 else:
-    from airflow.providers.common.compat.security.permissions import RESOURCE_ASSET
+    from airflow.providers.common.compat.security.permissions import RESOURCE_ASSET, RESOURCE_ASSET_ALIAS
 
 log = logging.getLogger(__name__)
 
@@ -233,6 +233,7 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_DAG_CODE),
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_DAG_RUN),
         (permissions.ACTION_CAN_READ, RESOURCE_ASSET),
+        (permissions.ACTION_CAN_READ, RESOURCE_ASSET_ALIAS),
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_CLUSTER_ACTIVITY),
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_POOL),
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_IMPORT_ERROR),
diff --git a/providers/fab/src/airflow/providers/fab/www/security/permissions.py b/providers/fab/src/airflow/providers/fab/www/security/permissions.py
index acd245865a4ad..33f0c28bc9a6b 100644
--- a/providers/fab/src/airflow/providers/fab/www/security/permissions.py
+++ b/providers/fab/src/airflow/providers/fab/www/security/permissions.py
@@ -34,6 +34,7 @@
 RESOURCE_DAG_WARNING = "DAG Warnings"
 RESOURCE_CLUSTER_ACTIVITY = "Cluster Activity"
 RESOURCE_ASSET = "Assets"
+RESOURCE_ASSET_ALIAS = "Asset Aliases"
 RESOURCE_DOCS = "Documentation"
 RESOURCE_DOCS_MENU = "Docs"
 RESOURCE_IMPORT_ERROR = "ImportError"
diff --git a/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py b/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
index 4bcfb4722e8c3..a50489b1610d9 100644
--- a/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
+++ b/providers/fab/tests/unit/fab/auth_manager/test_fab_auth_manager.py
@@ -45,7 +45,7 @@
     from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
     from airflow.providers.fab.auth_manager.security_manager.override import FabAirflowSecurityManagerOverride
 
-from airflow.providers.common.compat.security.permissions import RESOURCE_ASSET
+from airflow.providers.common.compat.security.permissions import RESOURCE_ASSET, RESOURCE_ASSET_ALIAS
 from airflow.providers.fab.www.security.permissions import (
     ACTION_CAN_ACCESS_MENU,
     ACTION_CAN_CREATE,
@@ -74,6 +74,7 @@
     "is_authorized_configuration": RESOURCE_CONFIG,
     "is_authorized_connection": RESOURCE_CONNECTION,
     "is_authorized_asset": RESOURCE_ASSET,
+    "is_authorized_asset_alias": RESOURCE_ASSET_ALIAS,
     "is_authorized_variable": RESOURCE_VARIABLE,
 }
 
diff --git a/providers/fab/tests/unit/fab/auth_manager/test_security.py b/providers/fab/tests/unit/fab/auth_manager/test_security.py
index 159131dc111f7..3c04138cf5a04 100644
--- a/providers/fab/tests/unit/fab/auth_manager/test_security.py
+++ b/providers/fab/tests/unit/fab/auth_manager/test_security.py
@@ -60,9 +60,9 @@
 from tests_common.test_utils.permissions import _resource_name
 
 if TYPE_CHECKING:
-    from airflow.providers.fab.www.security.permissions import RESOURCE_ASSET
+    from airflow.providers.fab.www.security.permissions import RESOURCE_ASSET, RESOURCE_ASSET_ALIAS
 else:
-    from airflow.providers.common.compat.security.permissions import RESOURCE_ASSET
+    from airflow.providers.common.compat.security.permissions import RESOURCE_ASSET, RESOURCE_ASSET_ALIAS
 
 
 pytestmark = pytest.mark.db_test
@@ -419,6 +419,7 @@ def test_get_user_roles_for_anonymous_user(app, security_manager):
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_DAG_CODE),
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_DAG_RUN),
         (permissions.ACTION_CAN_READ, RESOURCE_ASSET),
+        (permissions.ACTION_CAN_READ, RESOURCE_ASSET_ALIAS),
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_CLUSTER_ACTIVITY),
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_IMPORT_ERROR),
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_DAG_WARNING),
diff --git a/tests/api_fastapi/auth/managers/simple/test_simple_auth_manager.py b/tests/api_fastapi/auth/managers/simple/test_simple_auth_manager.py
index 2ad1eff7fba16..88d59e385374b 100644
--- a/tests/api_fastapi/auth/managers/simple/test_simple_auth_manager.py
+++ b/tests/api_fastapi/auth/managers/simple/test_simple_auth_manager.py
@@ -78,6 +78,7 @@ def test_serialize_user(self, auth_manager):
             "is_authorized_connection",
             "is_authorized_dag",
             "is_authorized_asset",
+            "is_authorized_asset_alias",
             "is_authorized_pool",
             "is_authorized_variable",
         ],
@@ -132,6 +133,7 @@ def test_is_authorized_view_methods(self, auth_manager, api, kwargs, role, resul
             "is_authorized_configuration",
             "is_authorized_connection",
             "is_authorized_asset",
+            "is_authorized_asset_alias",
             "is_authorized_pool",
             "is_authorized_variable",
         ],
@@ -173,7 +175,7 @@ def test_is_authorized_methods_user_role_required(self, auth_manager, api, role,
 
     @pytest.mark.parametrize(
         "api",
-        ["is_authorized_dag", "is_authorized_asset", "is_authorized_pool"],
+        ["is_authorized_dag", "is_authorized_asset", "is_authorized_asset_alias", "is_authorized_pool"],
     )
     @pytest.mark.parametrize(
         "role, method, result",
diff --git a/tests/api_fastapi/auth/managers/test_base_auth_manager.py b/tests/api_fastapi/auth/managers/test_base_auth_manager.py
index 3950fd49de066..82b1ffbd832aa 100644
--- a/tests/api_fastapi/auth/managers/test_base_auth_manager.py
+++ b/tests/api_fastapi/auth/managers/test_base_auth_manager.py
@@ -34,6 +34,7 @@
     from airflow.api_fastapi.auth.managers.base_auth_manager import ResourceMethod
     from airflow.api_fastapi.auth.managers.models.resource_details import (
         AccessView,
+        AssetAliasDetails,
         AssetDetails,
         ConfigurationDetails,
         DagAccessEntity,
@@ -95,6 +96,15 @@ def is_authorized_asset(
     ) -> bool:
         raise NotImplementedError()
 
+    def is_authorized_asset_alias(
+        self,
+        *,
+        method: ResourceMethod,
+        details: AssetAliasDetails | None = None,
+        user: BaseAuthManagerUserTest | None = None,
+    ) -> bool:
+        raise NotImplementedError()
+
     def is_authorized_pool(
         self,
         *,


Reviews:
user: login: jason810496
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-01T13:52:23Z
user: login: vincbeck
body: It looks good. Could you please add unit tests?
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T17:38:55Z
user: login: vincbeck
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T20:04:02Z
user: login: rawwar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T06:23:07Z
user: login: pierrejeambrun
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-10T09:51:45Z
user: login: Lee-W
body: Yep, this PR only get asset alias itself without resolving it into assets. 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-10T10:05:51Z
user: login: vincbeck
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T14:35:44Z
user: login: rawwar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T14:42:12Z
user: login: vincbeck
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T14:59:47Z

Review comments:
diff_hunk: @@ -82,6 +82,23 @@ def callback():
     return inner
 
 
+def requires_access_asset_alias(method: ResourceMethod) -> Callable:
path: airflow/api_fastapi/core_api/security.py
user: login: jason810496
body: Looks good to me.
 I thought we could leverage `requires_access_asset` but `AssetsAlias` have its own model and id, so `requires_access_asset_alias`  and  `AssetAliasDetails` are required I think.
created_at: 2025-03-01T13:50:36Z
author_association: MEMBER
diff_hunk: @@ -321,6 +321,11 @@ def is_authorized_asset(
     ) -> bool:
         return self._is_authorized(method=method, resource_type=RESOURCE_ASSET, user=user)
 
+    def is_authorized_asset_alias(
path: providers/fab/src/airflow/providers/fab/auth_manager/fab_auth_manager.py
user: login: vincbeck
body: Can you add a unit test for it?
created_at: 2025-03-07T20:03:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -181,6 +182,20 @@ def is_authorized_asset(
             user=user,
         )
 
+    def is_authorized_asset_alias(
path: airflow/auth/managers/simple/simple_auth_manager.py
user: login: vincbeck
body: Can you add a unit test for it?
created_at: 2025-03-07T20:04:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -181,6 +182,20 @@ def is_authorized_asset(
             user=user,
         )
 
+    def is_authorized_asset_alias(
path: airflow/auth/managers/simple/simple_auth_manager.py
user: login: rawwar
body: Done
created_at: 2025-03-10T06:23:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -233,6 +233,7 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_DAG_CODE),
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_DAG_RUN),
         (permissions.ACTION_CAN_READ, RESOURCE_ASSET),
+        (permissions.ACTION_CAN_READ, RESOURCE_ASSET_ALIAS),
path: providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
user: login: vincbeck
body: Sorry I missed that during the review but other permissions than READ need to be added to the OP role. @rawwar could you please create a PR for that?
created_at: 2025-03-11T14:35:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -233,6 +233,7 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_DAG_CODE),
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_DAG_RUN),
         (permissions.ACTION_CAN_READ, RESOURCE_ASSET),
+        (permissions.ACTION_CAN_READ, RESOURCE_ASSET_ALIAS),
path: providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
user: login: rawwar
body: @vincbeck , we only GET asset alias. No other verbs on asset aliad. Should i still add others?
created_at: 2025-03-11T14:42:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -233,6 +233,7 @@ class FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_DAG_CODE),
         (permissions.ACTION_CAN_READ, permissions.RESOURCE_DAG_RUN),
         (permissions.ACTION_CAN_READ, RESOURCE_ASSET),
+        (permissions.ACTION_CAN_READ, RESOURCE_ASSET_ALIAS),
path: providers/fab/src/airflow/providers/fab/auth_manager/security_manager/override.py
user: login: vincbeck
body: My bad :) All good then 👌 
created_at: 2025-03-11T14:59:46Z
author_association: CONTRIBUTOR

PR comments:
user: login: rawwar
created_at: 2025-03-07T15:51:09Z
body: @vincbeck , Since we are now exposing AssetAlias, both AWS auth manager and FAB need to implement `is_authorized_asset_alias`. Can I add them separately in a different PR or should I covert them here?
author_association: CONTRIBUTOR
user: login: rawwar
created_at: 2025-03-07T15:54:07Z
body: "Get alias" and "Get aliases" just show Id, name and group. No details regarding an asset. So, I think there's no need to check if the user has access to the asset referred by these aliases.

wdyt, @Lee-W , @uranusjr 
author_association: CONTRIBUTOR
user: login: vincbeck
created_at: 2025-03-07T16:00:50Z
body: > @vincbeck , Since we are now exposing AssetAlias, both AWS auth manager and FAB need to implement `is_authorized_asset_alias`. Can I add them separately in a different PR or should I covert them here?

You have to, otherwise mypy will not be happy 
author_association: CONTRIBUTOR
user: login: rawwar
created_at: 2025-03-07T18:49:07Z
body: failed tests do not seem to be related to the changes in this PR.

@vincbeck , can you please retrigger just the failed boto test?
author_association: CONTRIBUTOR
user: login: vincbeck
created_at: 2025-03-07T20:02:30Z
body: > failed tests do not seem to be related to the changes in this PR.
> 
> @vincbeck , can you please retrigger just the failed boto test?

#47512 is the fix
author_association: CONTRIBUTOR
user: login: uranusjr
created_at: 2025-03-10T05:52:32Z
body: > "Get alias" and "Get aliases" just show Id, name and group. No details regarding an asset. So, I think there's no need to check if the user has access to the asset referred by these aliases.

I agree with _Get Alias(es)_ do not need to check asset perm. However, we probably should check alias perms in _Get Asset(s)_ since it shows a list of aliases an asset is associated to. The list should only contain aliases the user has access to.

(This brings up the question if the alias responses should also include a list of associated assets, but that’s a question for another day, and perm check should be added to it if that’s implemented.)
author_association: MEMBER
user: login: Lee-W
created_at: 2025-03-10T06:09:10Z
body: > > "Get alias" and "Get aliases" just show Id, name and group. No details regarding an asset. So, I think there's no need to check if the user has access to the asset referred by these aliases.
> 
> I agree with _Get Alias(es)_ do not need to check asset perm. However, we probably should check alias perms in _Get Asset(s)_ since it shows a list of aliases an asset is associated to. The list should only contain aliases the user has access to.
> 
> (This brings up the question if the alias responses should also include a list of associated assets, but that’s a question for another day, and perm check should be added to it if that’s implemented.)

Agree. I think the idea is to ensure that users only access the objects they have permissions for. For the asset endpoint, if we return its alias, we should check the alias permission and vice versa.
author_association: MEMBER
user: login: pierrejeambrun
created_at: 2025-03-10T09:51:21Z
body: If I understand correctly that is good from an asset_alias endpoints point of views (TP + Wei) as well as from the AuthManager permissions (vincent).

The only thing missing is to add AssetAlias permission check on Asset endpoints ? (I think we can do that in a followup PR, this one focus on asset aliases and it seems to be completed ?)
author_association: MEMBER