Skip to content

Commit

Permalink
fix(charts): View in SQL Lab with relevant perm (#24903)
Browse files Browse the repository at this point in the history
  • Loading branch information
giftig authored Aug 10, 2023
1 parent bcd2493 commit ce65a3b
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 28 deletions.
51 changes: 37 additions & 14 deletions superset-frontend/src/dashboard/util/permissionUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
import { Dashboard } from 'src/types/Dashboard';
import Owner from 'src/types/Owner';
import {
canUserAccessSqlLab,
userHasPermission,
canUserEditDashboard,
canUserSaveAsDashboard,
isUserAdmin,
Expand Down Expand Up @@ -65,11 +65,18 @@ const owner: Owner = {
last_name: 'User',
};

const sqlLabMenuAccessPermission: [string, string] = ['menu_access', 'SQL Lab'];

const arbitraryPermissions: [string, string][] = [
['can_write', 'AnArbitraryView'],
sqlLabMenuAccessPermission,
];

const sqlLabUser: UserWithPermissionsAndRoles = {
...ownerUser,
roles: {
...ownerUser.roles,
sql_lab: [],
sql_lab: [sqlLabMenuAccessPermission],
},
};

Expand Down Expand Up @@ -139,24 +146,40 @@ test('isUserAdmin returns false for non-admin user', () => {
expect(isUserAdmin(ownerUser)).toEqual(false);
});

test('canUserAccessSqlLab returns true for admin user', () => {
expect(canUserAccessSqlLab(adminUser)).toEqual(true);
});

test('canUserAccessSqlLab returns false for undefined', () => {
expect(canUserAccessSqlLab(undefined)).toEqual(false);
test('userHasPermission always returns true for admin user', () => {
arbitraryPermissions.forEach(permissionView => {
expect(
userHasPermission(adminUser, permissionView[1], permissionView[0]),
).toEqual(true);
});
});

test('canUserAccessSqlLab returns false for undefined user', () => {
expect(canUserAccessSqlLab(undefinedUser)).toEqual(false);
test('userHasPermission always returns false for undefined user', () => {
arbitraryPermissions.forEach(permissionView => {
expect(
userHasPermission(undefinedUser, permissionView[1], permissionView[0]),
).toEqual(false);
});
});

test('canUserAccessSqlLab returns false for non-sqllab role', () => {
expect(canUserAccessSqlLab(ownerUser)).toEqual(false);
test('userHasPermission returns false if user does not have permission', () => {
expect(
userHasPermission(
ownerUser,
sqlLabMenuAccessPermission[1],
sqlLabMenuAccessPermission[0],
),
).toEqual(false);
});

test('canUserAccessSqlLab returns true for sqllab role', () => {
expect(canUserAccessSqlLab(sqlLabUser)).toEqual(true);
test('userHasPermission returns true if user has permission', () => {
expect(
userHasPermission(
sqlLabUser,
sqlLabMenuAccessPermission[1],
sqlLabMenuAccessPermission[0],
),
).toEqual(true);
});

describe('canUserSaveAsDashboard with RBAC feature flag disabled', () => {
Expand Down
17 changes: 11 additions & 6 deletions superset-frontend/src/dashboard/util/permissionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import { findPermission } from 'src/utils/findPermission';
// this should really be a config value,
// but is hardcoded in backend logic already, so...
const ADMIN_ROLE_NAME = 'admin';
const SQL_LAB_ROLE = 'sql_lab';

export const isUserAdmin = (
user?: UserWithPermissionsAndRoles | UndefinedUser,
Expand All @@ -53,15 +52,21 @@ export const canUserEditDashboard = (
(isUserAdmin(user) || isUserDashboardOwner(dashboard, user)) &&
findPermission('can_write', 'Dashboard', user?.roles);

export function canUserAccessSqlLab(
user?: UserWithPermissionsAndRoles | UndefinedUser,
export function userHasPermission(
user: UserWithPermissionsAndRoles | UndefinedUser,
viewName: string,
permissionName: string,
) {
return (
isUserAdmin(user) ||
(isUserWithPermissionsAndRoles(user) &&
Object.keys(user.roles || {}).some(
role => role.toLowerCase() === SQL_LAB_ROLE,
))
Object.values(user.roles || {})
.flat()
.some(
permissionView =>
permissionView[0] === permissionName &&
permissionView[1] === viewName,
))
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ test('Should show SQL Lab for sql_lab role', async () => {
isActive: true,
lastName: 'sql',
permissions: {},
roles: { Gamma: [], sql_lab: [] },
roles: { Gamma: [], sql_lab: [['menu_access', 'SQL Lab']] },
userId: 2,
username: 'sql',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip';
import { URL_PARAMS } from 'src/constants';
import { getDatasourceAsSaveableDataset } from 'src/utils/datasourceUtils';
import {
canUserAccessSqlLab,
userHasPermission,
isUserAdmin,
} from 'src/dashboard/util/permissionUtils';
import ModalTrigger from 'src/components/ModalTrigger';
Expand Down Expand Up @@ -283,7 +283,7 @@ class DatasourceControl extends React.PureComponent {
datasource.owners?.map(o => o.id || o.value).includes(user.userId) ||
isUserAdmin(user);

const canAccessSqlLab = canUserAccessSqlLab(user);
const canAccessSqlLab = userHasPermission(user, 'SQL Lab', 'menu_access');

const editText = t('Edit dataset');

Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/src/pages/Home/Home.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const mockedProps = {
isAnonymous: false,
permissions: {},
roles: {
sql_lab: [],
sql_lab: [['can_read', 'SavedQuery']],
},
},
};
Expand Down
8 changes: 4 additions & 4 deletions superset-frontend/src/pages/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import { AntdSwitch } from 'src/components';
import getBootstrapData from 'src/utils/getBootstrapData';
import { TableTab } from 'src/views/CRUD/types';
import SubMenu, { SubMenuProps } from 'src/features/home/SubMenu';
import { canUserAccessSqlLab } from 'src/dashboard/util/permissionUtils';
import { userHasPermission } from 'src/dashboard/util/permissionUtils';
import { WelcomePageLastTab } from 'src/features/home/types';
import ActivityTable from 'src/features/home/ActivityTable';
import ChartTable from 'src/features/home/ChartTable';
Expand Down Expand Up @@ -156,7 +156,7 @@ export const LoadingCards = ({ cover }: LoadingProps) => (
);

function Welcome({ user, addDangerToast }: WelcomeProps) {
const canAccessSqlLab = canUserAccessSqlLab(user);
const canReadSavedQueries = userHasPermission(user, 'SavedQuery', 'can_read');
const userid = user.userId;
const id = userid!.toString(); // confident that user is not a guest user
const params = rison.encode({ page_size: 6 });
Expand Down Expand Up @@ -281,7 +281,7 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
addDangerToast(t('There was an issue fetching your chart: %s', err));
return Promise.resolve();
}),
canAccessSqlLab
canReadSavedQueries
? getUserOwnedObjects(id, 'saved_query', ownSavedQueryFilters)
.then(r => {
setQueryData(r);
Expand Down Expand Up @@ -410,7 +410,7 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
/>
)}
</Collapse.Panel>
{canAccessSqlLab && (
{canReadSavedQueries && (
<Collapse.Panel header={t('Saved queries')} key="4">
{!queryData ? (
<LoadingCards cover={checked} />
Expand Down

0 comments on commit ce65a3b

Please sign in to comment.