This is an automated email from the ASF dual-hosted git repository.
pierrejeambrun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new f01c69a15ab fix(UI): show 403 error when unauthorized user reparses
DAG (#61560)
f01c69a15ab is described below
commit f01c69a15ab1a5d32d0917a793d438857c6b200b
Author: Liam <[email protected]>
AuthorDate: Wed Mar 11 06:27:35 2026 -0400
fix(UI): show 403 error when unauthorized user reparses DAG (#61560)
* fix(UI): show permission error when unauthorized user tries to reparse DAG
When a Viewer or Author without edit permission tries to reparse a DAG,
the API returns 403 but the UI shows a generic "Dag parsing request
failed" message. This checks the error status and shows an appropriate
"Access Denied" toast instead.
Closes #61459
Co-authored-by: Cursor <[email protected]>
* Generalize 403 permission toast handling in UI
Move the forbidden toast copy to common translations and route DAG reparse
mutation errors through a shared error toaster helper so 403 responses
consistently show a reusable access-denied message.
Made-with: Cursor
* Small adjustments
---------
Co-authored-by: Cursor <[email protected]>
Co-authored-by: pierrejeambrun <[email protected]>
---
.../airflow/ui/public/i18n/locales/en/common.json | 4 +++
.../ui/src/components/ui/createErrorToaster.ts | 40 ++++++++++++++++++++++
.../src/airflow/ui/src/queries/useDagParsing.ts | 15 ++++----
3 files changed, 50 insertions(+), 9 deletions(-)
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
b/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
index 745193fd071..89c5d48e816 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
@@ -313,6 +313,10 @@
"title": "Delete {{resourceName}} Request Submitted"
}
},
+ "forbidden": {
+ "description": "You do not have permission to perform this action.",
+ "title": "Access Denied"
+ },
"import": {
"error": "Import {{resourceName}} Request Failed",
"success": {
diff --git
a/airflow-core/src/airflow/ui/src/components/ui/createErrorToaster.ts
b/airflow-core/src/airflow/ui/src/components/ui/createErrorToaster.ts
new file mode 100644
index 00000000000..c59cabbf3b6
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/components/ui/createErrorToaster.ts
@@ -0,0 +1,40 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import type { TFunction } from "i18next";
+
+import type { ExpandedApiError } from "src/components/ErrorAlert";
+import { toaster } from "src/components/ui";
+
+type ErrorToastMessage = {
+ readonly description: string;
+ readonly title: string;
+};
+
+export const createErrorToaster =
+ (translate: TFunction, fallbackMessage: ErrorToastMessage) => (error:
unknown) => {
+ const isForbidden = (error as ExpandedApiError).status === 403;
+
+ toaster.create({
+ description: isForbidden
+ ? translate("toaster.forbidden.description", { ns: "common" })
+ : fallbackMessage.description,
+ title: isForbidden ? translate("toaster.forbidden.title", { ns: "common"
}) : fallbackMessage.title,
+ type: "error",
+ });
+ };
diff --git a/airflow-core/src/airflow/ui/src/queries/useDagParsing.ts
b/airflow-core/src/airflow/ui/src/queries/useDagParsing.ts
index 914799dfc54..2fc8c43c0e3 100644
--- a/airflow-core/src/airflow/ui/src/queries/useDagParsing.ts
+++ b/airflow-core/src/airflow/ui/src/queries/useDagParsing.ts
@@ -25,18 +25,15 @@ import {
UseDagSourceServiceGetDagSourceKeyFn,
} from "openapi/queries";
import { toaster } from "src/components/ui";
+import { createErrorToaster } from "src/components/ui/createErrorToaster";
export const useDagParsing = ({ dagId }: { readonly dagId: string }) => {
const queryClient = useQueryClient();
- const { t: translate } = useTranslation("dag");
-
- const onError = () => {
- toaster.create({
- description: translate("parse.toaster.error.description"),
- title: translate("parse.toaster.error.title"),
- type: "error",
- });
- };
+ const { t: translate } = useTranslation(["dag", "common"]);
+ const onError = createErrorToaster(translate, {
+ description: translate("parse.toaster.error.description"),
+ title: translate("parse.toaster.error.title"),
+ });
const onSuccess = async () => {
await queryClient.invalidateQueries({