From 9c5264af0d4040cfcfb13c7e668e3ad9c242dcf8 Mon Sep 17 00:00:00 2001 From: Amit Miran <47772523+amitmiran137@users.noreply.github.com> Date: Tue, 13 Apr 2021 13:54:14 +0300 Subject: [PATCH] feat(can_share): can share chart and dashboard (#14076) * feat: share chart - can_share_chart share dashboard can_share_dashboard * fix: pre-commit * fix: userCanShare tests * fix: after hugh CR * fix: adjust after spa refactor --- .../components/HeaderActionsDropdown_spec.jsx | 14 +++++---- .../src/dashboard/actions/hydrate.js | 10 +++++++ .../src/dashboard/components/Header.jsx | 2 ++ .../components/HeaderActionsDropdown.jsx | 22 ++++++++------ .../components/SliceHeader/index.tsx | 3 ++ .../components/SliceHeaderControls/index.jsx | 30 +++++++++++-------- .../components/gridComponents/Chart.jsx | 3 ++ .../src/dashboard/containers/Chart.jsx | 1 + superset/security/manager.py | 2 ++ tests/security_tests.py | 5 +++- 10 files changed, 64 insertions(+), 28 deletions(-) diff --git a/superset-frontend/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx b/superset-frontend/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx index ea3b65ca28..fa60986cea 100644 --- a/superset-frontend/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx +++ b/superset-frontend/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx @@ -67,7 +67,7 @@ describe('HeaderActionsDropdown', () => { } describe('readonly-user', () => { - const overrideProps = { userCanSave: false }; + const overrideProps = { userCanSave: false, userCanShare: false }; it('should render the DropdownButton', () => { const { wrapper } = setup(overrideProps); @@ -89,9 +89,9 @@ describe('HeaderActionsDropdown', () => { expect(menu.find(RefreshIntervalModal)).toExist(); }); - it('should render the ShareMenuItems', () => { + it('should not render the ShareMenuItems', () => { const { menu } = setup(overrideProps); - expect(menu.find(ShareMenuItems)).toExist(); + expect(menu.find(ShareMenuItems)).not.toExist(); }); it('should not render the CssEditor', () => { @@ -101,7 +101,7 @@ describe('HeaderActionsDropdown', () => { }); describe('write-user', () => { - const overrideProps = { userCanSave: true }; + const overrideProps = { userCanSave: true, userCanShare: true }; it('should render the DropdownButton', () => { const { wrapper } = setup(overrideProps); @@ -135,7 +135,11 @@ describe('HeaderActionsDropdown', () => { }); describe('write-user-with-edit-mode', () => { - const overrideProps = { userCanSave: true, editMode: true }; + const overrideProps = { + userCanSave: true, + editMode: true, + userCanShare: true, + }; it('should render the DropdownButton', () => { const { wrapper } = setup(overrideProps); diff --git a/superset-frontend/src/dashboard/actions/hydrate.js b/superset-frontend/src/dashboard/actions/hydrate.js index 7cdaa2396d..8065bf625f 100644 --- a/superset-frontend/src/dashboard/actions/hydrate.js +++ b/superset-frontend/src/dashboard/actions/hydrate.js @@ -313,7 +313,17 @@ export const hydrateDashboard = (dashboardData, chartData, datasourcesData) => ( userId: String(user.userId), // legacy, please use state.user instead dash_edit_perm: getPermissions('can_write', 'Dashboard', roles), dash_save_perm: getPermissions('can_save_dash', 'Superset', roles), + dash_share_perm: getPermissions( + 'can_share_dashboard', + 'Superset', + roles, + ), superset_can_explore: getPermissions('can_explore', 'Superset', roles), + superset_can_share: getPermissions( + 'can_share_chart', + 'Superset', + roles, + ), superset_can_csv: getPermissions('can_csv', 'Superset', roles), slice_can_edit: getPermissions('can_slice', 'Superset', roles), common: { diff --git a/superset-frontend/src/dashboard/components/Header.jsx b/superset-frontend/src/dashboard/components/Header.jsx index 6a89e94d5d..1e5d1868fe 100644 --- a/superset-frontend/src/dashboard/components/Header.jsx +++ b/superset-frontend/src/dashboard/components/Header.jsx @@ -375,6 +375,7 @@ class Header extends React.PureComponent { } = this.props; const userCanEdit = dashboardInfo.dash_edit_perm; + const userCanShare = dashboardInfo.dash_share_perm; const userCanSaveAs = dashboardInfo.dash_save_perm; const refreshLimit = dashboardInfo.common.conf.SUPERSET_DASHBOARD_PERIODICAL_REFRESH_LIMIT; @@ -542,6 +543,7 @@ class Header extends React.PureComponent { editMode={editMode} hasUnsavedChanges={hasUnsavedChanges} userCanEdit={userCanEdit} + userCanShare={userCanShare} userCanSave={userCanSaveAs} isLoading={isLoading} showPropertiesModal={this.showPropertiesModal} diff --git a/superset-frontend/src/dashboard/components/HeaderActionsDropdown.jsx b/superset-frontend/src/dashboard/components/HeaderActionsDropdown.jsx index 5d27f1f7f5..ba255fb322 100644 --- a/superset-frontend/src/dashboard/components/HeaderActionsDropdown.jsx +++ b/superset-frontend/src/dashboard/components/HeaderActionsDropdown.jsx @@ -54,6 +54,7 @@ const propTypes = { startPeriodicRender: PropTypes.func.isRequired, editMode: PropTypes.bool.isRequired, userCanEdit: PropTypes.bool.isRequired, + userCanShare: PropTypes.bool.isRequired, userCanSave: PropTypes.bool.isRequired, isLoading: PropTypes.bool.isRequired, layout: PropTypes.object.isRequired, @@ -192,6 +193,7 @@ class HeaderActionsDropdown extends React.PureComponent { expandedSlices, onSave, userCanEdit, + userCanShare, userCanSave, isLoading, refreshLimit, @@ -241,15 +243,17 @@ class HeaderActionsDropdown extends React.PureComponent { /> )} - + {userCanShare && ( + + )} = ({ isExpanded = [], sliceName = '', supersetCanExplore = false, + supersetCanShare = false, supersetCanCSV = false, sliceCanEdit = false, slice, @@ -172,6 +174,7 @@ const SliceHeader: FC = ({ exploreChart={exploreChart} exportCSV={exportCSV} supersetCanExplore={supersetCanExplore} + supersetCanShare={supersetCanShare} supersetCanCSV={supersetCanCSV} sliceCanEdit={sliceCanEdit} componentId={componentId} diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx index edef021377..650a590ac1 100644 --- a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx +++ b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx @@ -43,6 +43,7 @@ const propTypes = { isExpanded: PropTypes.bool, updatedDttm: PropTypes.number, supersetCanExplore: PropTypes.bool, + supersetCanShare: PropTypes.bool, supersetCanCSV: PropTypes.bool, sliceCanEdit: PropTypes.bool, toggleExpandSlice: PropTypes.func, @@ -61,6 +62,7 @@ const defaultProps = { isCached: [], isExpanded: false, supersetCanExplore: false, + supersetCanShare: false, supersetCanCSV: false, sliceCanEdit: false, }; @@ -72,7 +74,6 @@ const MENU_KEYS = { EXPLORE_CHART: 'explore_chart', EXPORT_CSV: 'export_csv', RESIZE_LABEL: 'resize_label', - SHARE_CHART: 'share_chart', DOWNLOAD_AS_IMAGE: 'download_as_image', }; @@ -188,6 +189,7 @@ class SliceHeaderControls extends React.PureComponent { addSuccessToast, addDangerToast, isFullSize, + supersetCanShare, } = this.props; const crossFilterItems = getChartMetadataRegistry().items; const isCrossFilter = Object.entries(crossFilterItems) @@ -253,18 +255,20 @@ class SliceHeaderControls extends React.PureComponent { )} - + {supersetCanShare && ( + + )} {resizeLabel} diff --git a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx index 18cec53418..bfe775eb8e 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx @@ -65,6 +65,7 @@ const propTypes = { isExpanded: PropTypes.bool.isRequired, isCached: PropTypes.bool, supersetCanExplore: PropTypes.bool.isRequired, + supersetCanShare: PropTypes.bool.isRequired, supersetCanCSV: PropTypes.bool.isRequired, sliceCanEdit: PropTypes.bool.isRequired, addSuccessToast: PropTypes.func.isRequired, @@ -256,6 +257,7 @@ export default class Chart extends React.Component { toggleExpandSlice, timeout, supersetCanExplore, + supersetCanShare, supersetCanCSV, sliceCanEdit, addSuccessToast, @@ -311,6 +313,7 @@ export default class Chart extends React.Component { updateSliceName={updateSliceName} sliceName={sliceName} supersetCanExplore={supersetCanExplore} + supersetCanShare={supersetCanShare} supersetCanCSV={supersetCanCSV} sliceCanEdit={sliceCanEdit} componentId={componentId} diff --git a/superset-frontend/src/dashboard/containers/Chart.jsx b/superset-frontend/src/dashboard/containers/Chart.jsx index 94f88d3a9e..31c0e3255c 100644 --- a/superset-frontend/src/dashboard/containers/Chart.jsx +++ b/superset-frontend/src/dashboard/containers/Chart.jsx @@ -84,6 +84,7 @@ function mapStateToProps( editMode: dashboardState.editMode, isExpanded: !!dashboardState.expandedSlices[id], supersetCanExplore: !!dashboardInfo.superset_can_explore, + supersetCanShare: !!dashboardInfo.superset_can_share, supersetCanCSV: !!dashboardInfo.superset_can_csv, sliceCanEdit: !!dashboardInfo.slice_can_edit, ownCurrentState: dataMask.ownFilters?.[id]?.currentState, diff --git a/superset/security/manager.py b/superset/security/manager.py index 1c4419cfc5..6472af6844 100644 --- a/superset/security/manager.py +++ b/superset/security/manager.py @@ -556,6 +556,8 @@ class SupersetSecurityManager( # pylint: disable=too-many-public-methods self.add_permission_view_menu("all_datasource_access", "all_datasource_access") self.add_permission_view_menu("all_database_access", "all_database_access") self.add_permission_view_menu("all_query_access", "all_query_access") + self.add_permission_view_menu("can_share_dashboard", "Superset") + self.add_permission_view_menu("can_share_chart", "Superset") def create_missing_perms(self) -> None: """ diff --git a/tests/security_tests.py b/tests/security_tests.py index 0648104101..f7e55174db 100644 --- a/tests/security_tests.py +++ b/tests/security_tests.py @@ -670,12 +670,13 @@ class TestRolePermission(SupersetTestCase): self.assertIn(("can_csv", "Superset"), perm_set) self.assertIn(("can_dashboard", "Superset"), perm_set) self.assertIn(("can_explore", "Superset"), perm_set) + self.assertIn(("can_share_chart", "Superset"), perm_set) + self.assertIn(("can_share_dashboard", "Superset"), perm_set) self.assertIn(("can_explore_json", "Superset"), perm_set) self.assertIn(("can_fave_dashboards", "Superset"), perm_set) self.assertIn(("can_fave_slices", "Superset"), perm_set) self.assertIn(("can_save_dash", "Superset"), perm_set) self.assertIn(("can_slice", "Superset"), perm_set) - self.assertIn(("can_explore", "Superset"), perm_set) self.assertIn(("can_explore_json", "Superset"), perm_set) self.assertIn(("can_userinfo", "UserDBModelView"), perm_set) self.assert_can_menu("Databases", perm_set) @@ -868,6 +869,8 @@ class TestRolePermission(SupersetTestCase): self.assertIn(("can_csv", "Superset"), gamma_perm_set) self.assertIn(("can_dashboard", "Superset"), gamma_perm_set) self.assertIn(("can_explore", "Superset"), gamma_perm_set) + self.assertIn(("can_share_chart", "Superset"), gamma_perm_set) + self.assertIn(("can_share_dashboard", "Superset"), gamma_perm_set) self.assertIn(("can_explore_json", "Superset"), gamma_perm_set) self.assertIn(("can_fave_dashboards", "Superset"), gamma_perm_set) self.assertIn(("can_fave_slices", "Superset"), gamma_perm_set)