From c99843b13abf781ffa8d34da1ace460aed17bf3a Mon Sep 17 00:00:00 2001
From: Vitor Avila <96086495+Vitor-Avila@users.noreply.github.com>
Date: Thu, 24 Jul 2025 10:45:31 -0300
Subject: [PATCH] fix: Hide View in SQL Lab for users without access (#34293)
---
.../components/controls/ViewQuery.test.tsx | 38 ++++++++++++++++++-
.../explore/components/controls/ViewQuery.tsx | 10 ++++-
2 files changed, 45 insertions(+), 3 deletions(-)
diff --git a/superset-frontend/src/explore/components/controls/ViewQuery.test.tsx b/superset-frontend/src/explore/components/controls/ViewQuery.test.tsx
index 9a18020c342..76da555d746 100644
--- a/superset-frontend/src/explore/components/controls/ViewQuery.test.tsx
+++ b/superset-frontend/src/explore/components/controls/ViewQuery.test.tsx
@@ -25,6 +25,7 @@ import {
} from 'spec/helpers/testing-library';
import fetchMock from 'fetch-mock';
import copyTextToClipboard from 'src/utils/copy';
+import { RootState } from 'src/dashboard/types';
import ViewQuery, { ViewQueryProps } from './ViewQuery';
const mockHistoryPush = jest.fn();
@@ -37,8 +38,29 @@ jest.mock('react-router-dom', () => ({
jest.mock('src/utils/copy');
-function setup(props: ViewQueryProps) {
- return render(, { useRouter: true, useRedux: true });
+const mockState = (
+ roles: Record = {
+ Admin: [['menu_access', 'SQL Lab']],
+ },
+) =>
+ ({
+ user: {
+ firstName: 'Test',
+ isActive: true,
+ isAnonymous: false,
+ lastName: 'User',
+ username: 'testuser',
+ permissions: {} as Record,
+ roles,
+ },
+ }) as Partial;
+
+function setup(props: ViewQueryProps, state: Partial = mockState()) {
+ return render(, {
+ useRouter: true,
+ useRedux: true,
+ initialState: state,
+ });
}
const mockProps = {
@@ -156,3 +178,15 @@ test('opens SQL Lab in a new tab when View in SQL Lab button is clicked with met
'_blank',
);
});
+
+test('hides View in SQL Lab button when user does not have SQL Lab access', () => {
+ setup(
+ mockProps,
+ mockState({
+ Basic: [['menu_access', 'Dashboard']],
+ }),
+ );
+
+ expect(screen.queryByText('View in SQL Lab')).not.toBeInTheDocument();
+ expect(screen.getByText('Copy')).toBeInTheDocument(); // Copy button should still be visible
+});
diff --git a/superset-frontend/src/explore/components/controls/ViewQuery.tsx b/superset-frontend/src/explore/components/controls/ViewQuery.tsx
index dca607e8461..ab6de84ce22 100644
--- a/superset-frontend/src/explore/components/controls/ViewQuery.tsx
+++ b/superset-frontend/src/explore/components/controls/ViewQuery.tsx
@@ -24,11 +24,14 @@ import {
useEffect,
useState,
} from 'react';
+import { useSelector } from 'react-redux';
import rison from 'rison';
import { styled, SupersetClient, t } from '@superset-ui/core';
import { Icons, Switch, Button, Skeleton } from '@superset-ui/core/components';
import { CopyToClipboard } from 'src/components';
+import { RootState } from 'src/dashboard/types';
import { CopyButton } from 'src/explore/components/DataTableControl';
+import { findPermission } from 'src/utils/findPermission';
import CodeSyntaxHighlighter, {
SupportedLanguage,
preloadLanguages,
@@ -89,6 +92,9 @@ const ViewQuery: FC = props => {
const [showFormatSQL, setShowFormatSQL] = useState(true);
const history = useHistory();
const currentSQL = (showFormatSQL ? formattedSQL : sql) ?? sql;
+ const canAccessSQLLab = useSelector((state: RootState) =>
+ findPermission('menu_access', 'SQL Lab', state.user?.roles),
+ );
// Preload the language when component mounts to ensure smooth experience
useEffect(() => {
@@ -162,7 +168,9 @@ const ViewQuery: FC = props => {
}
/>
-
+ {canAccessSQLLab && (
+
+ )}