fix(charts): View in SQL Lab with relevant perm (#24903)

This commit is contained in:
Rob Moore 2023-08-10 18:04:07 +01:00 committed by GitHub
parent bcd24936bc
commit ce65a3b9cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 56 additions and 28 deletions

View File

@ -24,7 +24,7 @@ import {
import { Dashboard } from 'src/types/Dashboard'; import { Dashboard } from 'src/types/Dashboard';
import Owner from 'src/types/Owner'; import Owner from 'src/types/Owner';
import { import {
canUserAccessSqlLab, userHasPermission,
canUserEditDashboard, canUserEditDashboard,
canUserSaveAsDashboard, canUserSaveAsDashboard,
isUserAdmin, isUserAdmin,
@ -65,11 +65,18 @@ const owner: Owner = {
last_name: 'User', last_name: 'User',
}; };
const sqlLabMenuAccessPermission: [string, string] = ['menu_access', 'SQL Lab'];
const arbitraryPermissions: [string, string][] = [
['can_write', 'AnArbitraryView'],
sqlLabMenuAccessPermission,
];
const sqlLabUser: UserWithPermissionsAndRoles = { const sqlLabUser: UserWithPermissionsAndRoles = {
...ownerUser, ...ownerUser,
roles: { roles: {
...ownerUser.roles, ...ownerUser.roles,
sql_lab: [], sql_lab: [sqlLabMenuAccessPermission],
}, },
}; };
@ -139,24 +146,40 @@ test('isUserAdmin returns false for non-admin user', () => {
expect(isUserAdmin(ownerUser)).toEqual(false); expect(isUserAdmin(ownerUser)).toEqual(false);
}); });
test('canUserAccessSqlLab returns true for admin user', () => { test('userHasPermission always returns true for admin user', () => {
expect(canUserAccessSqlLab(adminUser)).toEqual(true); arbitraryPermissions.forEach(permissionView => {
expect(
userHasPermission(adminUser, permissionView[1], permissionView[0]),
).toEqual(true);
});
}); });
test('canUserAccessSqlLab returns false for undefined', () => { test('userHasPermission always returns false for undefined user', () => {
expect(canUserAccessSqlLab(undefined)).toEqual(false); arbitraryPermissions.forEach(permissionView => {
expect(
userHasPermission(undefinedUser, permissionView[1], permissionView[0]),
).toEqual(false);
});
}); });
test('canUserAccessSqlLab returns false for undefined user', () => { test('userHasPermission returns false if user does not have permission', () => {
expect(canUserAccessSqlLab(undefinedUser)).toEqual(false); expect(
userHasPermission(
ownerUser,
sqlLabMenuAccessPermission[1],
sqlLabMenuAccessPermission[0],
),
).toEqual(false);
}); });
test('canUserAccessSqlLab returns false for non-sqllab role', () => { test('userHasPermission returns true if user has permission', () => {
expect(canUserAccessSqlLab(ownerUser)).toEqual(false); expect(
}); userHasPermission(
sqlLabUser,
test('canUserAccessSqlLab returns true for sqllab role', () => { sqlLabMenuAccessPermission[1],
expect(canUserAccessSqlLab(sqlLabUser)).toEqual(true); sqlLabMenuAccessPermission[0],
),
).toEqual(true);
}); });
describe('canUserSaveAsDashboard with RBAC feature flag disabled', () => { describe('canUserSaveAsDashboard with RBAC feature flag disabled', () => {

View File

@ -28,7 +28,6 @@ import { findPermission } from 'src/utils/findPermission';
// this should really be a config value, // this should really be a config value,
// but is hardcoded in backend logic already, so... // but is hardcoded in backend logic already, so...
const ADMIN_ROLE_NAME = 'admin'; const ADMIN_ROLE_NAME = 'admin';
const SQL_LAB_ROLE = 'sql_lab';
export const isUserAdmin = ( export const isUserAdmin = (
user?: UserWithPermissionsAndRoles | UndefinedUser, user?: UserWithPermissionsAndRoles | UndefinedUser,
@ -53,15 +52,21 @@ export const canUserEditDashboard = (
(isUserAdmin(user) || isUserDashboardOwner(dashboard, user)) && (isUserAdmin(user) || isUserDashboardOwner(dashboard, user)) &&
findPermission('can_write', 'Dashboard', user?.roles); findPermission('can_write', 'Dashboard', user?.roles);
export function canUserAccessSqlLab( export function userHasPermission(
user?: UserWithPermissionsAndRoles | UndefinedUser, user: UserWithPermissionsAndRoles | UndefinedUser,
viewName: string,
permissionName: string,
) { ) {
return ( return (
isUserAdmin(user) || isUserAdmin(user) ||
(isUserWithPermissionsAndRoles(user) && (isUserWithPermissionsAndRoles(user) &&
Object.keys(user.roles || {}).some( Object.values(user.roles || {})
role => role.toLowerCase() === SQL_LAB_ROLE, .flat()
)) .some(
permissionView =>
permissionView[0] === permissionName &&
permissionView[1] === viewName,
))
); );
} }

View File

@ -149,7 +149,7 @@ test('Should show SQL Lab for sql_lab role', async () => {
isActive: true, isActive: true,
lastName: 'sql', lastName: 'sql',
permissions: {}, permissions: {},
roles: { Gamma: [], sql_lab: [] }, roles: { Gamma: [], sql_lab: [['menu_access', 'SQL Lab']] },
userId: 2, userId: 2,
username: 'sql', username: 'sql',
}, },

View File

@ -43,7 +43,7 @@ import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip';
import { URL_PARAMS } from 'src/constants'; import { URL_PARAMS } from 'src/constants';
import { getDatasourceAsSaveableDataset } from 'src/utils/datasourceUtils'; import { getDatasourceAsSaveableDataset } from 'src/utils/datasourceUtils';
import { import {
canUserAccessSqlLab, userHasPermission,
isUserAdmin, isUserAdmin,
} from 'src/dashboard/util/permissionUtils'; } from 'src/dashboard/util/permissionUtils';
import ModalTrigger from 'src/components/ModalTrigger'; import ModalTrigger from 'src/components/ModalTrigger';
@ -283,7 +283,7 @@ class DatasourceControl extends React.PureComponent {
datasource.owners?.map(o => o.id || o.value).includes(user.userId) || datasource.owners?.map(o => o.id || o.value).includes(user.userId) ||
isUserAdmin(user); isUserAdmin(user);
const canAccessSqlLab = canUserAccessSqlLab(user); const canAccessSqlLab = userHasPermission(user, 'SQL Lab', 'menu_access');
const editText = t('Edit dataset'); const editText = t('Edit dataset');

View File

@ -109,7 +109,7 @@ const mockedProps = {
isAnonymous: false, isAnonymous: false,
permissions: {}, permissions: {},
roles: { roles: {
sql_lab: [], sql_lab: [['can_read', 'SavedQuery']],
}, },
}, },
}; };

View File

@ -50,7 +50,7 @@ import { AntdSwitch } from 'src/components';
import getBootstrapData from 'src/utils/getBootstrapData'; import getBootstrapData from 'src/utils/getBootstrapData';
import { TableTab } from 'src/views/CRUD/types'; import { TableTab } from 'src/views/CRUD/types';
import SubMenu, { SubMenuProps } from 'src/features/home/SubMenu'; 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 { WelcomePageLastTab } from 'src/features/home/types';
import ActivityTable from 'src/features/home/ActivityTable'; import ActivityTable from 'src/features/home/ActivityTable';
import ChartTable from 'src/features/home/ChartTable'; import ChartTable from 'src/features/home/ChartTable';
@ -156,7 +156,7 @@ export const LoadingCards = ({ cover }: LoadingProps) => (
); );
function Welcome({ user, addDangerToast }: WelcomeProps) { function Welcome({ user, addDangerToast }: WelcomeProps) {
const canAccessSqlLab = canUserAccessSqlLab(user); const canReadSavedQueries = userHasPermission(user, 'SavedQuery', 'can_read');
const userid = user.userId; const userid = user.userId;
const id = userid!.toString(); // confident that user is not a guest user const id = userid!.toString(); // confident that user is not a guest user
const params = rison.encode({ page_size: 6 }); const params = rison.encode({ page_size: 6 });
@ -281,7 +281,7 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
addDangerToast(t('There was an issue fetching your chart: %s', err)); addDangerToast(t('There was an issue fetching your chart: %s', err));
return Promise.resolve(); return Promise.resolve();
}), }),
canAccessSqlLab canReadSavedQueries
? getUserOwnedObjects(id, 'saved_query', ownSavedQueryFilters) ? getUserOwnedObjects(id, 'saved_query', ownSavedQueryFilters)
.then(r => { .then(r => {
setQueryData(r); setQueryData(r);
@ -410,7 +410,7 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
/> />
)} )}
</Collapse.Panel> </Collapse.Panel>
{canAccessSqlLab && ( {canReadSavedQueries && (
<Collapse.Panel header={t('Saved queries')} key="4"> <Collapse.Panel header={t('Saved queries')} key="4">
{!queryData ? ( {!queryData ? (
<LoadingCards cover={checked} /> <LoadingCards cover={checked} />