From a0c20ac7aaf5b5667b4bb78d270825e039995431 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Thu, 3 Jun 2021 11:58:25 -0400 Subject: [PATCH] [Dashboard] Fix Copy To Permission & Unskip RBAC tests (#100616) * slightly better typing for dashboard permissions. Fixed typo, unskipped functional tests --- src/plugins/dashboard/common/types.ts | 8 ++++++ .../application/dashboard_app_functions.ts | 4 +-- .../embeddable/dashboard_container.tsx | 6 ++--- .../dashboard_empty_screen.test.tsx.snap | 4 +++ .../empty_screen/dashboard_empty_screen.tsx | 6 ++++- .../hooks/use_dashboard_container.test.tsx | 4 +-- .../listing/dashboard_listing.test.tsx | 4 +-- .../application/top_nav/show_share_modal.tsx | 6 ++--- .../dashboard/public/application/types.ts | 4 +-- src/plugins/dashboard/public/plugin.tsx | 8 ++++-- .../dashboard/server/capabilities_provider.ts | 6 ++++- .../feature_controls/dashboard_security.ts | 26 +++++++++++-------- .../time_to_visualize_security.ts | 3 +-- 13 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/plugins/dashboard/common/types.ts b/src/plugins/dashboard/common/types.ts index 9a6d185ef2ac..5851ffa045bc 100644 --- a/src/plugins/dashboard/common/types.ts +++ b/src/plugins/dashboard/common/types.ts @@ -32,6 +32,14 @@ export interface DashboardPanelState< panelRefName?: string; } +export interface DashboardCapabilities { + showWriteControls: boolean; + saveQuery: boolean; + createNew: boolean; + show: boolean; + [key: string]: boolean; +} + /** * This should always represent the latest dashboard panel shape, after all possible migrations. */ diff --git a/src/plugins/dashboard/public/application/dashboard_app_functions.ts b/src/plugins/dashboard/public/application/dashboard_app_functions.ts index 6d51422d4bd2..895a56242bf9 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_functions.ts +++ b/src/plugins/dashboard/public/application/dashboard_app_functions.ts @@ -21,7 +21,7 @@ import { switchMap, } from 'rxjs/operators'; -import { DashboardCapabilities } from './types'; +import { DashboardAppCapabilities } from './types'; import { DashboardConstants } from '../dashboard_constants'; import { DashboardStateManager } from './dashboard_state_manager'; import { convertSavedDashboardPanelToPanelState } from '../../common/embeddable/embeddable_saved_object_converters'; @@ -103,7 +103,7 @@ export const getDashboardContainerInput = ({ dashboardStateManager, dashboardCapabilities, }: { - dashboardCapabilities: DashboardCapabilities; + dashboardCapabilities: DashboardAppCapabilities; dashboardStateManager: DashboardStateManager; incomingEmbeddable?: EmbeddablePackageState; lastReloadRequestTime?: number; diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index 92b0727d2458..847a190a6e08 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -37,11 +37,11 @@ import { } from '../../services/kibana_react'; import { PLACEHOLDER_EMBEDDABLE } from './placeholder'; import { PanelPlacementMethod, IPanelPlacementArgs } from './panel/dashboard_panel_placement'; -import { DashboardCapabilities } from '../types'; +import { DashboardAppCapabilities } from '../types'; import { PresentationUtilPluginStart } from '../../services/presentation_util'; export interface DashboardContainerInput extends ContainerInput { - dashboardCapabilities?: DashboardCapabilities; + dashboardCapabilities?: DashboardAppCapabilities; refreshConfig?: RefreshInterval; isEmbeddedExternally?: boolean; isFullScreenMode: boolean; @@ -91,7 +91,7 @@ export interface InheritedChildInput extends IndexSignature { export type DashboardReactContextValue = KibanaReactContextValue; export type DashboardReactContext = KibanaReactContext; -const defaultCapabilities: DashboardCapabilities = { +const defaultCapabilities: DashboardAppCapabilities = { show: false, createNew: false, saveQuery: false, diff --git a/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap b/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap index 44beed5e4a89..ae8943e9f6b3 100644 --- a/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap +++ b/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap @@ -590,10 +590,12 @@ exports[`DashboardEmptyScreen renders correctly with readonly mode 1`] = `
{ return ( - + toasts: coreMock.createStart().notifications.toasts, }); -const defaultCapabilities: DashboardCapabilities = { +const defaultCapabilities: DashboardAppCapabilities = { show: false, createNew: false, saveQuery: false, diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing.test.tsx b/src/plugins/dashboard/public/application/listing/dashboard_listing.test.tsx index 022c830b180b..febb03d58d93 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_listing.test.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_listing.test.tsx @@ -24,7 +24,7 @@ import { savedObjectsPluginMock } from '../../../../saved_objects/public/mocks'; import { DashboardListing, DashboardListingProps } from './dashboard_listing'; import { embeddablePluginMock } from '../../../../embeddable/public/mocks'; import { visualizationsPluginMock } from '../../../../visualizations/public/mocks'; -import { DashboardAppServices, DashboardCapabilities } from '../types'; +import { DashboardAppServices, DashboardAppCapabilities } from '../types'; import { dataPluginMock } from '../../../../data/public/mocks'; import { chromeServiceMock, coreMock } from '../../../../../core/public/mocks'; import { I18nProvider } from '@kbn/i18n/react'; @@ -59,7 +59,7 @@ function makeDefaultServices(): DashboardAppServices { return { savedObjects: savedObjectsPluginMock.createStartContract(), embeddable: embeddablePluginMock.createInstance().doStart(), - dashboardCapabilities: {} as DashboardCapabilities, + dashboardCapabilities: {} as DashboardAppCapabilities, initializerContext: {} as PluginInitializerContext, chrome: chromeServiceMock.createStartContract(), navigation: {} as NavigationPublicPluginStart, diff --git a/src/plugins/dashboard/public/application/top_nav/show_share_modal.tsx b/src/plugins/dashboard/public/application/top_nav/show_share_modal.tsx index 56823adf6bc1..a96b1ebd4f1f 100644 --- a/src/plugins/dashboard/public/application/top_nav/show_share_modal.tsx +++ b/src/plugins/dashboard/public/application/top_nav/show_share_modal.tsx @@ -16,7 +16,7 @@ import { SharePluginStart } from '../../services/share'; import { dashboardUrlParams } from '../dashboard_router'; import { DashboardStateManager } from '../dashboard_state_manager'; import { shareModalStrings } from '../../dashboard_strings'; -import { DashboardCapabilities } from '../types'; +import { DashboardAppCapabilities } from '../types'; const showFilterBarId = 'showFilterBar'; @@ -24,14 +24,14 @@ interface ShowShareModalProps { share: SharePluginStart; anchorElement: HTMLElement; savedDashboard: DashboardSavedObject; - dashboardCapabilities: DashboardCapabilities; + dashboardCapabilities: DashboardAppCapabilities; dashboardStateManager: DashboardStateManager; } export const showPublicUrlSwitch = (anonymousUserCapabilities: Capabilities) => { if (!anonymousUserCapabilities.dashboard) return false; - const dashboard = (anonymousUserCapabilities.dashboard as unknown) as DashboardCapabilities; + const dashboard = (anonymousUserCapabilities.dashboard as unknown) as DashboardAppCapabilities; return !!dashboard.show; }; diff --git a/src/plugins/dashboard/public/application/types.ts b/src/plugins/dashboard/public/application/types.ts index dd291291ce9d..aae8a1f6eca5 100644 --- a/src/plugins/dashboard/public/application/types.ts +++ b/src/plugins/dashboard/public/application/types.ts @@ -49,7 +49,7 @@ export interface DashboardSaveOptions { isTitleDuplicateConfirmed: boolean; } -export interface DashboardCapabilities { +export interface DashboardAppCapabilities { visualizeCapabilities: { save: boolean }; mapsCapabilities: { save: boolean }; hideWriteControls: boolean; @@ -77,7 +77,7 @@ export interface DashboardAppServices { usageCollection?: UsageCollectionSetup; navigation: NavigationPublicPluginStart; dashboardPanelStorage: DashboardPanelStorage; - dashboardCapabilities: DashboardCapabilities; + dashboardCapabilities: DashboardAppCapabilities; initializerContext: PluginInitializerContext; onAppLeave: AppMountParameters['onAppLeave']; savedObjectsTagging?: SavedObjectsTaggingApi; diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 230918399d88..b73fe5f2ba41 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -65,6 +65,7 @@ import { AddToLibraryAction, LibraryNotificationAction, CopyToDashboardAction, + DashboardCapabilities, } from './application'; import { createDashboardUrlGenerator, @@ -351,6 +352,9 @@ export class DashboardPlugin const { notifications, overlays, application } = core; const { uiActions, data, share, presentationUtil, embeddable } = plugins; + const dashboardCapabilities: Readonly = application.capabilities + .dashboard as DashboardCapabilities; + const SavedObjectFinder = getSavedObjectFinder(core.savedObjects, core.uiSettings); const expandPanelAction = new ExpandPanelAction(); @@ -395,8 +399,8 @@ export class DashboardPlugin overlays, embeddable.getStateTransfer(), { - canCreateNew: Boolean(application.capabilities.dashboard.createNew), - canEditExisting: !Boolean(application.capabilities.dashboard.hideWriteControls), + canCreateNew: Boolean(dashboardCapabilities.createNew), + canEditExisting: Boolean(dashboardCapabilities.showWriteControls), }, presentationUtil.ContextProvider ); diff --git a/src/plugins/dashboard/server/capabilities_provider.ts b/src/plugins/dashboard/server/capabilities_provider.ts index 25457c1a487d..c5b740c58129 100644 --- a/src/plugins/dashboard/server/capabilities_provider.ts +++ b/src/plugins/dashboard/server/capabilities_provider.ts @@ -6,7 +6,11 @@ * Side Public License, v 1. */ -export const capabilitiesProvider = () => ({ +import { DashboardCapabilities } from '../common/types'; + +export const capabilitiesProvider = (): { + dashboard: DashboardCapabilities; +} => ({ dashboard: { createNew: true, show: true, diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts index bdbfb5050a32..94a0eedd07c5 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts @@ -31,8 +31,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const queryBar = getService('queryBar'); const savedQueryManagementComponent = getService('savedQueryManagementComponent'); - // FLAKY: https://github.com/elastic/kibana/issues/86950 - describe.skip('dashboard feature controls security', () => { + describe('dashboard feature controls security', () => { before(async () => { await esArchiver.load('dashboard/feature_controls/security'); await esArchiver.loadIfNeeded('logstash_functional'); @@ -86,7 +85,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('only shows the dashboard navlink', async () => { const navLinks = await appsMenu.readLinks(); - expect(navLinks.map((link) => link.text)).to.eql(['Overview', 'Dashboard']); + expect(navLinks.map((link) => link.text)).to.eql([ + 'Overview', + 'Dashboard', + 'Stack Management', + ]); }); it(`landing page shows "Create new Dashboard" button`, async () => { @@ -108,8 +111,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await globalNav.badgeMissingOrFail(); }); - // Can't figure out how to get this test to pass - it.skip(`create new dashboard shows addNew button`, async () => { + it(`create new dashboard shows addNew button`, async () => { await PageObjects.common.navigateToActualUrl( 'dashboard', DashboardConstants.CREATE_NEW_DASHBOARD_URL, @@ -320,8 +322,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await globalNav.badgeExistsOrFail('Read only'); }); - // Has this behavior changed? - it.skip(`create new dashboard redirects to the home page`, async () => { + it(`create new dashboard shows the read only warning`, async () => { await PageObjects.common.navigateToActualUrl( 'dashboard', DashboardConstants.CREATE_NEW_DASHBOARD_URL, @@ -330,7 +331,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { shouldLoginIfPrompted: false, } ); - await testSubjects.existOrFail('homeApp', { timeout: 20000 }); + await testSubjects.existOrFail('dashboardEmptyReadOnly', { timeout: 20000 }); }); it(`can view existing Dashboard`, async () => { @@ -347,6 +348,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); + it('does not allow copy to dashboard behaviour', async () => { + await panelActions.expectMissingPanelAction('embeddablePanelAction-copyToDashboard'); + }); + it(`Permalinks doesn't show create short-url button`, async () => { await PageObjects.share.openShareMenuItem('Permalinks'); await PageObjects.share.createShortUrlMissingOrFail(); @@ -438,8 +443,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await globalNav.badgeExistsOrFail('Read only'); }); - // Has this behavior changed? - it.skip(`create new dashboard redirects to the home page`, async () => { + it(`create new dashboard shows the read only warning`, async () => { await PageObjects.common.navigateToActualUrl( 'dashboard', DashboardConstants.CREATE_NEW_DASHBOARD_URL, @@ -448,7 +452,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { shouldLoginIfPrompted: false, } ); - await testSubjects.existOrFail('homeApp', { timeout: 20000 }); + await testSubjects.existOrFail('dashboardEmptyReadOnly', { timeout: 20000 }); }); it(`can view existing Dashboard`, async () => { diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts index 2c151235518e..730c00a8d5e4 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/time_to_visualize_security.ts @@ -29,8 +29,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const security = getService('security'); const find = getService('find'); - // flaky https://github.com/elastic/kibana/issues/98249 - describe.skip('dashboard time to visualize security', () => { + describe('dashboard time to visualize security', () => { before(async () => { await esArchiver.load('dashboard/feature_controls/security'); await esArchiver.loadIfNeeded('logstash_functional');