diff --git a/x-pack/solutions/search/plugins/enterprise_search/common/__mocks__/initial_app_data.ts b/x-pack/solutions/search/plugins/enterprise_search/common/__mocks__/initial_app_data.ts index 5b6b847643206..8b139d1f42a7c 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/common/__mocks__/initial_app_data.ts +++ b/x-pack/solutions/search/plugins/enterprise_search/common/__mocks__/initial_app_data.ts @@ -27,10 +27,6 @@ export const DEFAULT_INITIAL_APP_DATA = { }, }, }, - access: { - hasAppSearchAccess: true, - hasWorkplaceSearchAccess: true, - }, features: { hasConnectors: true, hasDefaultIngestPipeline: true, diff --git a/x-pack/solutions/search/plugins/enterprise_search/common/types/index.ts b/x-pack/solutions/search/plugins/enterprise_search/common/types/index.ts index 364008ef18b49..9254d99784ee2 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/common/types/index.ts +++ b/x-pack/solutions/search/plugins/enterprise_search/common/types/index.ts @@ -15,7 +15,6 @@ import { } from './workplace_search'; export interface InitialAppData { - access?: ProductAccess; appSearch?: AppSearchAccount; configuredLimits?: ConfiguredLimits; enterpriseSearchVersion?: string; @@ -31,11 +30,6 @@ export interface ConfiguredLimits { workplaceSearch: WorkplaceSearchConfiguredLimits; } -export interface ProductAccess { - hasAppSearchAccess: boolean; - hasWorkplaceSearchAccess: boolean; -} - export interface ProductFeatures { hasConnectors: boolean; hasDefaultIngestPipeline: boolean; diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts b/x-pack/solutions/search/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts index 222373178be4a..47ec6a83f3583 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts @@ -60,10 +60,6 @@ export const mockKibanaValues = { } as unknown as LensPublicStart, ml: mlPluginMock.createStartContract(), navigateToUrl: jest.fn(), - productAccess: { - hasAppSearchAccess: true, - hasWorkplaceSearchAccess: true, - }, productFeatures: { hasDocumentLevelSecurityEnabled: true, hasIncrementalSyncEnabled: true, diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_logic.ts b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_logic.ts index 3c1e531b1c6d9..c5497d3a3e135 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_logic.ts +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/new_search_index_logic.ts @@ -8,7 +8,6 @@ import { kea, MakeLogicType } from 'kea'; import { Actions } from '../../../shared/api_logic/create_api_logic'; -import { KibanaLogic } from '../../../shared/kibana/kibana_logic'; import { AddConnectorApiLogic, AddConnectorApiLogicArgs, @@ -88,15 +87,12 @@ export const NewSearchIndexLogic = kea ({ apiIndexCreated: () => { - if (!KibanaLogic.values.productAccess.hasAppSearchAccess) return; flashIndexCreatedToast(); }, connectorIndexCreated: () => { - if (!KibanaLogic.values.productAccess.hasAppSearchAccess) return; flashIndexCreatedToast(); }, crawlerIndexCreated: () => { - if (!KibanaLogic.values.productAccess.hasAppSearchAccess) return; flashIndexCreatedToast(); }, setRawName: async (_, breakpoint) => { diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/index.tsx b/x-pack/solutions/search/plugins/enterprise_search/public/applications/index.tsx index 019fbec6fd85f..dcade40199398 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/index.tsx @@ -25,7 +25,7 @@ import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { Router } from '@kbn/shared-ux-router'; import { DEFAULT_PRODUCT_FEATURES } from '../../common/constants'; -import { ClientConfigType, InitialAppData, ProductAccess } from '../../common/types'; +import { ClientConfigType, InitialAppData } from '../../common/types'; import { PluginsStart, ClientData, ESConfig, UpdateSideNavDefinitionFn } from '../plugin'; import { externalUrl } from './shared/enterprise_search_url'; @@ -59,7 +59,6 @@ export const renderApp = ( { config, data, esConfig }: { config: ClientConfigType; data: ClientData; esConfig: ESConfig } ) => { const { - access, appSearch, configuredLimits, enterpriseSearchVersion, @@ -89,12 +88,6 @@ export const renderApp = ( const entCloudHost = getCloudEnterpriseSearchHost(plugins.cloud); externalUrl.enterpriseSearchUrl = publicUrl || entCloudHost || config.host || ''; - const noProductAccess: ProductAccess = { - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, - }; - - const productAccess = access || noProductAccess; const productFeatures = features ?? { ...DEFAULT_PRODUCT_FEATURES }; const EmptyContext: FC> = ({ children }) => <>{children}; @@ -128,7 +121,6 @@ export const renderApp = ( lens, ml, navigateToUrl, - productAccess, productFeatures, renderHeaderActions: (HeaderActions) => params.setHeaderActionMenu( @@ -170,7 +162,6 @@ export const renderApp = ( ; - productAccess: ProductAccess; productFeatures: ProductFeatures; renderHeaderActions(HeaderActions?: FC): void; security?: SecurityPluginStart; @@ -100,7 +99,6 @@ export interface KibanaValues { lens: LensPublicStart | null; ml: MlPluginStart | null; navigateToUrl(path: string, options?: CreateHrefOptions): Promise; - productAccess: ProductAccess; productFeatures: ProductFeatures; renderHeaderActions(HeaderActions?: FC): void; security: SecurityPluginStart | null; @@ -146,7 +144,6 @@ export const KibanaLogic = kea>({ }, {}, ], - productAccess: [props.productAccess, {}], productFeatures: [props.productFeatures, {}], renderHeaderActions: [props.renderHeaderActions, {}], security: [props.security || null, {}], diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx b/x-pack/solutions/search/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx index 19a0fea68969b..bee329b62953a 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx @@ -20,7 +20,6 @@ import { renderHook } from '@testing-library/react'; import { EuiSideNavItemType } from '@elastic/eui'; import { DEFAULT_PRODUCT_FEATURES } from '../../../../common/constants'; -import { ProductAccess } from '../../../../common/types'; import { useEnterpriseSearchNav, @@ -28,10 +27,6 @@ import { useEnterpriseSearchAnalyticsNav, } from './nav'; -const DEFAULT_PRODUCT_ACCESS: ProductAccess = { - hasAppSearchAccess: true, - hasWorkplaceSearchAccess: true, -}; const baseNavItems = [ expect.objectContaining({ 'data-test-subj': 'searchSideNav-Home', @@ -186,16 +181,6 @@ const mockNavLinks = [ title: 'Inference Endpoints', url: '/app/elasticsearch/relevance/inference_endpoints', }, - { - id: 'appSearch:engines', - title: 'App Search', - url: '/app/enterprise_search/app_search', - }, - { - id: 'workplaceSearch', - title: 'Workplace Search', - url: '/app/enterprise_search/workplace_search', - }, { id: 'enterpriseSearchElasticsearch', title: 'Elasticsearch', @@ -221,7 +206,6 @@ const mockNavLinks = [ const defaultMockValues = { hasEnterpriseLicense: true, isSidebarEnabled: true, - productAccess: DEFAULT_PRODUCT_ACCESS, productFeatures: DEFAULT_PRODUCT_FEATURES, }; @@ -233,11 +217,7 @@ describe('useEnterpriseSearchContentNav', () => { }); it('returns an array of top-level Enterprise Search nav items', () => { - const fullProductAccess: ProductAccess = DEFAULT_PRODUCT_ACCESS; - setMockValues({ - ...defaultMockValues, - productAccess: fullProductAccess, - }); + setMockValues(defaultMockValues); const { result } = renderHook(() => useEnterpriseSearchNav()); diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx b/x-pack/solutions/search/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx index 13694a3f7db64..1a4a509d960d8 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx @@ -71,10 +71,6 @@ export const mockKibanaProps: KibanaLogicProps = { } as unknown as LensPublicStart, ml: mlPluginMock.createStartContract(), navigateToUrl: jest.fn(), - productAccess: { - hasAppSearchAccess: true, - hasWorkplaceSearchAccess: true, - }, productFeatures: { hasConnectors: true, hasDefaultIngestPipeline: true, diff --git a/x-pack/solutions/search/plugins/enterprise_search/server/lib/check_access.test.ts b/x-pack/solutions/search/plugins/enterprise_search/server/lib/check_access.test.ts deleted file mode 100644 index ef956ae85f689..0000000000000 --- a/x-pack/solutions/search/plugins/enterprise_search/server/lib/check_access.test.ts +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { spacesMock } from '@kbn/spaces-plugin/server/mocks'; - -import { GlobalConfigService } from '../services/global_config_service'; - -import { checkAccess } from './check_access'; - -jest.mock('./enterprise_search_config_api', () => ({ - callEnterpriseSearchConfigAPI: jest.fn(), -})); -import { callEnterpriseSearchConfigAPI } from './enterprise_search_config_api'; - -const enabledSpace = { - id: 'space', - name: 'space', - disabledFeatures: [], -}; - -const disabledSpace = { - id: 'space', - name: 'space', - disabledFeatures: ['enterpriseSearch'], -}; - -describe('checkAccess', () => { - const mockSecurity = { - authz: { - mode: { - useRbacForRequest: () => true, - }, - checkPrivilegesWithRequest: () => ({ - globally: () => ({ - hasAllRequested: false, - }), - }), - actions: { - ui: { - get: () => null, - }, - }, - }, - }; - const mockSpaces = spacesMock.createStart(); - const mockDependencies = { - request: { auth: { isAuthenticated: true } }, - config: { - host: 'http://localhost:3002', - }, - globalConfigService: new GlobalConfigService(), - security: mockSecurity, - spaces: mockSpaces, - } as any; - - describe('when security is disabled', () => { - it('should deny all access', async () => { - const security = { - authz: { mode: { useRbacForRequest: () => false } }, - }; - expect(await checkAccess({ ...mockDependencies, security })).toEqual({ - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, - }); - }); - }); - - describe('when the current request is unauthenticated', () => { - it('should deny all access', async () => { - const request = { - auth: { isAuthenticated: false }, - }; - expect(await checkAccess({ ...mockDependencies, request })).toEqual({ - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, - }); - }); - }); - - describe('when the space is disabled', () => { - it('should deny all access', async () => { - mockSpaces.spacesService.getActiveSpace.mockResolvedValueOnce(disabledSpace); - expect(await checkAccess({ ...mockDependencies })).toEqual({ - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, - }); - }); - }); - - describe('when the Spaces plugin is unavailable', () => { - describe('when getActiveSpace returns 403 forbidden', () => { - it('should deny all access', async () => { - mockSpaces.spacesService.getActiveSpace.mockReturnValueOnce( - Promise.reject({ output: { statusCode: 403 } }) - ); - expect(await checkAccess({ ...mockDependencies })).toEqual({ - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, - }); - }); - }); - - describe('when getActiveSpace throws', () => { - it('should re-throw', async () => { - mockSpaces.spacesService.getActiveSpace.mockReturnValueOnce(Promise.reject('Error')); - let expectedError = ''; - try { - await checkAccess({ ...mockDependencies }); - } catch (e) { - expectedError = e; - } - expect(expectedError).toEqual('Error'); - }); - }); - - describe('when spaces plugin is not available', () => { - it('should not throw', async () => { - await expect(checkAccess({ ...mockDependencies, spaces: undefined })).resolves.toEqual({ - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, - }); - }); - }); - }); - - describe('when the space is enabled', () => { - beforeEach(() => { - mockSpaces.spacesService.getActiveSpace.mockResolvedValueOnce(enabledSpace); - }); - - describe('when the user is a superuser', () => { - it('should allow all access when enabled at the space ', async () => { - const security = { - ...mockSecurity, - authz: { - mode: { useRbacForRequest: () => true }, - checkPrivilegesWithRequest: () => ({ - globally: () => ({ - hasAllRequested: true, - }), - }), - actions: { ui: { get: () => {} } }, - }, - }; - expect(await checkAccess({ ...mockDependencies, security })).toEqual({ - hasAppSearchAccess: true, - hasWorkplaceSearchAccess: true, - }); - }); - - it('falls back to assuming a non-superuser role if auth credentials are missing', async () => { - const security = { - authz: { - ...mockSecurity.authz, - checkPrivilegesWithRequest: () => ({ - globally: () => Promise.reject({ statusCode: 403 }), - }), - }, - }; - expect(await checkAccess({ ...mockDependencies, security })).toEqual({ - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, - }); - }); - - it('throws other authz errors', async () => { - const security = { - authz: { - ...mockSecurity.authz, - checkPrivilegesWithRequest: undefined, - }, - }; - await expect(checkAccess({ ...mockDependencies, security })).rejects.toThrow(); - }); - }); - - describe('when the user is a non-superuser', () => { - describe('when enterpriseSearch.host is not set in kibana.yml', () => { - it('should deny all access', async () => { - const config = { host: undefined }; - expect(await checkAccess({ ...mockDependencies, config })).toEqual({ - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, - }); - }); - }); - - describe('when enterpriseSearch.host is set in kibana.yml', () => { - it('should make a http call and return the access response', async () => { - (callEnterpriseSearchConfigAPI as jest.Mock).mockImplementationOnce(() => ({ - access: { - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: true, - }, - })); - expect(await checkAccess(mockDependencies)).toEqual({ - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: true, - }); - }); - - it('falls back to no access if no http response', async () => { - (callEnterpriseSearchConfigAPI as jest.Mock).mockImplementationOnce(() => ({})); - expect(await checkAccess(mockDependencies)).toEqual({ - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, - }); - }); - - it('falls back to no access if response error', async () => { - (callEnterpriseSearchConfigAPI as jest.Mock).mockImplementationOnce(() => ({ - responseStatus: 500, - responseStatusText: 'failed', - })); - expect(await checkAccess(mockDependencies)).toEqual({ - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, - }); - }); - }); - }); - }); -}); diff --git a/x-pack/solutions/search/plugins/enterprise_search/server/lib/check_access.ts b/x-pack/solutions/search/plugins/enterprise_search/server/lib/check_access.ts deleted file mode 100644 index af8cc16c6febe..0000000000000 --- a/x-pack/solutions/search/plugins/enterprise_search/server/lib/check_access.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { KibanaRequest, Logger } from '@kbn/core/server'; - -import { SecurityPluginSetup } from '@kbn/security-plugin/server'; -import { SpacesPluginStart } from '@kbn/spaces-plugin/server'; - -import { ConfigType } from '..'; -import { ProductAccess } from '../../common/types'; - -import { callEnterpriseSearchConfigAPI } from './enterprise_search_config_api'; - -interface CheckAccess { - request: KibanaRequest; - security: SecurityPluginSetup; - spaces?: SpacesPluginStart; - config: ConfigType; - log: Logger; -} - -const ALLOW_ALL_PLUGINS: ProductAccess = { - hasAppSearchAccess: true, - hasWorkplaceSearchAccess: true, -}; -const DENY_ALL_PLUGINS: ProductAccess = { - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, -}; - -/** - * Determines whether the user has access to our Enterprise Search products - * via HTTP call. If not, we hide the corresponding plugin links from the - * nav and catalogue in `plugin.ts`, which disables plugin access - */ -export const checkAccess = async ({ - config, - security, - spaces, - request, - log, -}: CheckAccess): Promise => { - const isRbacEnabled = security.authz.mode.useRbacForRequest(request); - - // If security has been disabled, always hide app search and workplace search - if (!isRbacEnabled) { - return DENY_ALL_PLUGINS; - } - - // We can only retrieve the active space when security is enabled and the request has already been authenticated - const attemptSpaceRetrieval = request.auth.isAuthenticated && !!spaces; - let allowedAtSpace = false; - - if (attemptSpaceRetrieval) { - try { - const space = await spaces.spacesService.getActiveSpace(request); - allowedAtSpace = !space.disabledFeatures?.includes('enterpriseSearch'); - } catch (err) { - if (err?.output?.statusCode === 403) { - allowedAtSpace = false; - } else { - throw err; - } - } - } - - // Hide the plugin if turned off in the current space. - if (!allowedAtSpace) { - return DENY_ALL_PLUGINS; - } - - // If the user is a "superuser" or has the base Kibana all privilege globally, always show the plugin - const isSuperUser = async (): Promise => { - try { - const { hasAllRequested } = await security.authz - .checkPrivilegesWithRequest(request) - .globally({ kibana: security.authz.actions.ui.get('enterpriseSearch', 'all') }); - return hasAllRequested || false; - } catch (err) { - if (err.statusCode === 401 || err.statusCode === 403) { - return false; - } - throw err; - } - }; - if (await isSuperUser()) { - return ALLOW_ALL_PLUGINS; - } - - // Hide the plugin when enterpriseSearch.host is not defined in kibana.yml - if (!config.host) { - return DENY_ALL_PLUGINS; - } - - // When enterpriseSearch.host is defined in kibana.yml, - // make a HTTP call which returns product access - const response = (await callEnterpriseSearchConfigAPI({ request, config, log })) || {}; - return 'access' in response ? response.access || DENY_ALL_PLUGINS : DENY_ALL_PLUGINS; -}; diff --git a/x-pack/solutions/search/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts b/x-pack/solutions/search/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts index 956d7f34277ce..c66cd66348f5f 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts +++ b/x-pack/solutions/search/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts @@ -128,10 +128,6 @@ describe('callEnterpriseSearchConfigAPI', () => { expect(await callEnterpriseSearchConfigAPI(mockDependencies)).toEqual({ ...DEFAULT_INITIAL_APP_DATA, kibanaVersion: '1.0.0', - access: { - hasAppSearchAccess: true, - hasWorkplaceSearchAccess: false, - }, features: { hasNativeConnectors: true, hasWebCrawler: true, @@ -145,10 +141,6 @@ describe('callEnterpriseSearchConfigAPI', () => { expect(await callEnterpriseSearchConfigAPI(mockDependencies)).toEqual({ kibanaVersion: '1.0.0', - access: { - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, - }, features: { hasNativeConnectors: true, hasWebCrawler: true, @@ -217,10 +209,6 @@ describe('callEnterpriseSearchConfigAPI', () => { }; expect(await callEnterpriseSearchConfigAPI({ ...mockDependencies, config })).toEqual({ - access: { - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, - }, features: { hasConnectors: false, hasDefaultIngestPipeline: false, diff --git a/x-pack/solutions/search/plugins/enterprise_search/server/lib/enterprise_search_config_api.ts b/x-pack/solutions/search/plugins/enterprise_search/server/lib/enterprise_search_config_api.ts index 7b4af80f3f2e5..c3fd144096da0 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/server/lib/enterprise_search_config_api.ts +++ b/x-pack/solutions/search/plugins/enterprise_search/server/lib/enterprise_search_config_api.ts @@ -45,10 +45,6 @@ export const callEnterpriseSearchConfigAPI = async ({ if (!config.host) // Return Access and Features for when running without `ent-search` return { - access: { - hasAppSearchAccess: false, - hasWorkplaceSearchAccess: false, - }, features: { hasConnectors: config.hasConnectors, hasDefaultIngestPipeline: config.hasDefaultIngestPipeline, @@ -100,10 +96,6 @@ export const callEnterpriseSearchConfigAPI = async ({ return { enterpriseSearchVersion: data?.version?.number, kibanaVersion: kibanaPackageJson.version, - access: { - hasAppSearchAccess: !!data?.current_user?.access?.app_search, - hasWorkplaceSearchAccess: !!data?.current_user?.access?.workplace_search, - }, features: { hasConnectors: config.hasConnectors, hasDefaultIngestPipeline: config.hasDefaultIngestPipeline,