diff --git a/airflow/api_fastapi/core_api/openapi/v1-generated.yaml b/airflow/api_fastapi/core_api/openapi/v1-generated.yaml index 1c73f2af0d104..ea50f2d864ccf 100644 --- a/airflow/api_fastapi/core_api/openapi/v1-generated.yaml +++ b/airflow/api_fastapi/core_api/openapi/v1-generated.yaml @@ -3790,12 +3790,16 @@ paths: summary: Get Extra Links description: Get extra links for task instance. operationId: get_extra_links + security: + - OAuth2PasswordBearer: [] parameters: - name: dag_id in: path required: true schema: - type: string + anyOf: + - type: string + - type: 'null' title: Dag Id - name: dag_run_id in: path diff --git a/airflow/api_fastapi/core_api/routes/public/extra_links.py b/airflow/api_fastapi/core_api/routes/public/extra_links.py index f612502c46d63..a52bcef26fe52 100644 --- a/airflow/api_fastapi/core_api/routes/public/extra_links.py +++ b/airflow/api_fastapi/core_api/routes/public/extra_links.py @@ -19,13 +19,14 @@ from typing import TYPE_CHECKING -from fastapi import HTTPException, Request, status +from fastapi import Depends, HTTPException, Request, status from sqlalchemy.sql import select from airflow.api_fastapi.common.db.common import SessionDep from airflow.api_fastapi.common.router import AirflowRouter from airflow.api_fastapi.core_api.datamodels.extra_links import ExtraLinksResponse from airflow.api_fastapi.core_api.openapi.exceptions import create_openapi_http_exception_doc +from airflow.api_fastapi.core_api.security import DagAccessEntity, requires_access_dag from airflow.exceptions import TaskNotFound if TYPE_CHECKING: @@ -40,6 +41,7 @@ @extra_links_router.get( "", responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]), + dependencies=[Depends(requires_access_dag("GET", DagAccessEntity.TASK_INSTANCE))], tags=["Task Instance"], ) def get_extra_links( diff --git a/airflow/ui/openapi-gen/requests/types.gen.ts b/airflow/ui/openapi-gen/requests/types.gen.ts index f3578ab98a9c8..a102621915b6f 100644 --- a/airflow/ui/openapi-gen/requests/types.gen.ts +++ b/airflow/ui/openapi-gen/requests/types.gen.ts @@ -2100,7 +2100,7 @@ export type GetEventLogsData = { export type GetEventLogsResponse = EventLogCollectionResponse; export type GetExtraLinksData = { - dagId: string; + dagId: string | null; dagRunId: string; mapIndex?: number; taskId: string; diff --git a/tests/api_fastapi/core_api/routes/public/test_extra_links.py b/tests/api_fastapi/core_api/routes/public/test_extra_links.py index 0907a2877565a..547673e4d2926 100644 --- a/tests/api_fastapi/core_api/routes/public/test_extra_links.py +++ b/tests/api_fastapi/core_api/routes/public/test_extra_links.py @@ -259,6 +259,20 @@ def test_should_respond_200_mapped_task_instance(self, test_client): "Google Custom": "http://google.com/custom_base_link?search=TEST_LINK_VALUE_1" } + def test_should_respond_401_unauthenticated(self, unauthenticated_test_client): + response = unauthenticated_test_client.get( + f"/public/dags/{self.dag_id}/dagRuns/{self.dag_run_id}/taskInstances/{self.task_single_link}/links", + ) + + assert response.status_code == 401 + + def test_should_respond_403_unauthorized(self, unauthorized_test_client): + response = unauthorized_test_client.get( + f"/public/dags/{self.dag_id}/dagRuns/{self.dag_run_id}/taskInstances/{self.task_single_link}/links", + ) + + assert response.status_code == 403 + def test_should_respond_404_invalid_map_index(self, test_client): response = test_client.get( f"/public/dags/{self.dag_id}/dagRuns/{self.dag_run_id}/taskInstances/{self.task_mapped}/links",