diff --git a/.sass-lint.yml b/.sass-lint.yml index db895583eb8a7c..eb43af293c670e 100644 --- a/.sass-lint.yml +++ b/.sass-lint.yml @@ -4,7 +4,6 @@ files: - 'src/legacy/core_plugins/timelion/**/*.s+(a|c)ss' - 'src/plugins/vis_type_vislib/**/*.s+(a|c)ss' - 'src/plugins/vis_type_xy/**/*.s+(a|c)ss' - - 'x-pack/legacy/plugins/security/**/*.s+(a|c)ss' - 'x-pack/plugins/canvas/**/*.s+(a|c)ss' - 'x-pack/plugins/triggers_actions_ui/**/*.s+(a|c)ss' - 'x-pack/plugins/lens/**/*.s+(a|c)ss' @@ -12,6 +11,7 @@ files: - 'x-pack/legacy/plugins/maps/**/*.s+(a|c)ss' - 'x-pack/plugins/maps/**/*.s+(a|c)ss' - 'x-pack/plugins/spaces/**/*.s+(a|c)ss' + - 'x-pack/plugins/security/**/*.s+(a|c)ss' ignore: - 'x-pack/plugins/canvas/shareable_runtime/**/*.s+(a|c)ss' rules: diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index e453abb3a0f98b..b7ad3e5f7fad71 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -39,7 +39,7 @@ "xpack.reporting": ["plugins/reporting"], "xpack.rollupJobs": ["legacy/plugins/rollup", "plugins/rollup"], "xpack.searchProfiler": "plugins/searchprofiler", - "xpack.security": ["legacy/plugins/security", "plugins/security"], + "xpack.security": "plugins/security", "xpack.server": "legacy/server", "xpack.securitySolution": "plugins/security_solution", "xpack.snapshotRestore": "plugins/snapshot_restore", diff --git a/x-pack/README.md b/x-pack/README.md index 744d97ca02c75e..03d2e3287c0f0b 100644 --- a/x-pack/README.md +++ b/x-pack/README.md @@ -25,8 +25,8 @@ Examples: - Run the jest test case whose description matches 'filtering should skip values of null': `cd x-pack && yarn test:jest -t 'filtering should skip values of null' plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.test.js` - Run the x-pack api integration test case whose description matches the given string: - `node scripts/functional_tests_server --config x-pack/test/api_integration/config.js` - `node scripts/functional_test_runner --config x-pack/test/api_integration/config.js --grep='apis Monitoring Beats list with restarted beat instance should load multiple clusters'` + `node scripts/functional_tests_server --config x-pack/test/api_integration/config.ts` + `node scripts/functional_test_runner --config x-pack/test/api_integration/config.ts --grep='apis Monitoring Beats list with restarted beat instance should load multiple clusters'` In addition to to providing a regular expression argument, specific tests can also be run by appeding `.only` to an `it` or `describe` function block. E.g. `describe(` to `describe.only(`. @@ -63,7 +63,7 @@ yarn test:mocha For more info, see [the Elastic functional test development guide](https://www.elastic.co/guide/en/kibana/current/development-functional-tests.html). -The functional UI tests, the API integration tests, and the SAML API integration tests are all run against a live browser, Kibana, and Elasticsearch install. Each set of tests is specified with a unique config that describes how to start the Elasticsearch server, the Kibana server, and what tests to run against them. The sets of tests that exist today are *functional UI tests* ([specified by this config](test/functional/config.js)), *API integration tests* ([specified by this config](test/api_integration/config.js)), and *SAML API integration tests* ([specified by this config](test/saml_api_integration/config.js)). +The functional UI tests, the API integration tests, and the SAML API integration tests are all run against a live browser, Kibana, and Elasticsearch install. Each set of tests is specified with a unique config that describes how to start the Elasticsearch server, the Kibana server, and what tests to run against them. The sets of tests that exist today are *functional UI tests* ([specified by this config](test/functional/config.js)), *API integration tests* ([specified by this config](test/api_integration/config.ts)), and *SAML API integration tests* ([specified by this config](test/saml_api_integration/config.ts)). The script runs all sets of tests sequentially like so: * builds Elasticsearch and X-Pack diff --git a/x-pack/legacy/plugins/beats_management/readme.md b/x-pack/legacy/plugins/beats_management/readme.md index 301caad683dd57..3414f09deed463 100644 --- a/x-pack/legacy/plugins/beats_management/readme.md +++ b/x-pack/legacy/plugins/beats_management/readme.md @@ -15,7 +15,7 @@ In one shell, from **~/kibana/x-pack**: `node scripts/functional_tests-server.js` In another shell, from **~kibana/x-pack**: -`node ../scripts/functional_test_runner.js --config test/api_integration/config.js`. +`node ../scripts/functional_test_runner.js --config test/api_integration/config.ts`. ### Manual e2e testing diff --git a/x-pack/legacy/plugins/beats_management/server/lib/adapters/framework/adapter_types.ts b/x-pack/legacy/plugins/beats_management/server/lib/adapters/framework/adapter_types.ts index bb1f68e1c03b37..80599f38d982ab 100644 --- a/x-pack/legacy/plugins/beats_management/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/legacy/plugins/beats_management/server/lib/adapters/framework/adapter_types.ts @@ -8,6 +8,7 @@ import { Lifecycle, ResponseToolkit } from 'hapi'; import * as t from 'io-ts'; +import { SecurityPluginSetup } from '../../../../../../../plugins/security/server'; import { LicenseType } from '../../../../common/constants/security'; export const internalAuthData = Symbol('internalAuthData'); @@ -39,6 +40,11 @@ export interface BackendFrameworkAdapter { } export interface KibanaLegacyServer { + newPlatform: { + setup: { + plugins: { security: SecurityPluginSetup }; + }; + }; plugins: { xpack_main: { status: { @@ -53,9 +59,6 @@ export interface KibanaLegacyServer { }; }; }; - security: { - getUser: (request: KibanaServerRequest) => any; - }; elasticsearch: { status: { on: (status: 'green' | 'yellow' | 'red', callback: () => void) => void; diff --git a/x-pack/legacy/plugins/beats_management/server/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/legacy/plugins/beats_management/server/lib/adapters/framework/kibana_framework_adapter.ts index 589f34ac746014..1bf9bbb22b3525 100644 --- a/x-pack/legacy/plugins/beats_management/server/lib/adapters/framework/kibana_framework_adapter.ts +++ b/x-pack/legacy/plugins/beats_management/server/lib/adapters/framework/kibana_framework_adapter.ts @@ -8,6 +8,7 @@ import { ResponseToolkit } from 'hapi'; import { PathReporter } from 'io-ts/lib/PathReporter'; import { get } from 'lodash'; import { isLeft } from 'fp-ts/lib/Either'; +import { KibanaRequest, LegacyRequest } from '../../../../../../../../src/core/server'; // @ts-ignore import { mirrorPluginStatus } from '../../../../../../server/lib/mirror_plugin_status'; import { @@ -128,13 +129,10 @@ export class KibanaBackendFrameworkAdapter implements BackendFrameworkAdapter { } private async getUser(request: KibanaServerRequest): Promise { - let user; - try { - user = await this.server.plugins.security.getUser(request); - } catch (e) { - return null; - } - if (user === null) { + const user = this.server.newPlatform.setup.plugins.security?.authc.getCurrentUser( + KibanaRequest.from((request as unknown) as LegacyRequest) + ); + if (!user) { return null; } const assertKibanaUser = RuntimeKibanaUser.decode(user); diff --git a/x-pack/legacy/plugins/security/index.ts b/x-pack/legacy/plugins/security/index.ts index 41371fcbc4c652..addeef34f63bfe 100644 --- a/x-pack/legacy/plugins/security/index.ts +++ b/x-pack/legacy/plugins/security/index.ts @@ -6,64 +6,17 @@ import { Root } from 'joi'; import { resolve } from 'path'; -import { Server } from 'src/legacy/server/kbn_server'; -import { KibanaRequest, LegacyRequest } from '../../../../src/core/server'; -// @ts-ignore -import { watchStatusAndLicenseToInitialize } from '../../server/lib/watch_status_and_license_to_initialize'; -import { AuthenticatedUser, SecurityPluginSetup } from '../../../plugins/security/server'; - -/** - * Public interface of the security plugin. - */ -export interface SecurityPlugin { - getUser: (request: LegacyRequest) => Promise; -} - -function getSecurityPluginSetup(server: Server) { - const securityPlugin = server.newPlatform.setup.plugins.security as SecurityPluginSetup; - if (!securityPlugin) { - throw new Error('Kibana Platform Security plugin is not available.'); - } - - return securityPlugin; -} export const security = (kibana: Record) => new kibana.Plugin({ id: 'security', publicDir: resolve(__dirname, 'public'), - require: ['kibana', 'xpack_main'], + require: ['kibana'], configPrefix: 'xpack.security', - uiExports: { - hacks: ['plugins/security/hacks/legacy'], - injectDefaultVars: (server: Server) => { - return { enableSpaceAwarePrivileges: server.config().get('xpack.spaces.enabled') }; - }, - }, - - config(Joi: Root) { - return Joi.object({ - enabled: Joi.boolean().default(true), - }) + uiExports: { hacks: ['plugins/security/hacks/legacy'] }, + config: (Joi: Root) => + Joi.object({ enabled: Joi.boolean().default(true) }) .unknown() - .default(); - }, - - async postInit(server: Server) { - watchStatusAndLicenseToInitialize(server.plugins.xpack_main, this, async () => { - const xpackInfo = server.plugins.xpack_main.info; - if (xpackInfo.isAvailable() && xpackInfo.feature('security').isEnabled()) { - await getSecurityPluginSetup(server).__legacyCompat.registerPrivilegesWithCluster(); - } - }); - }, - - async init(server: Server) { - const securityPlugin = getSecurityPluginSetup(server); - - server.expose({ - getUser: async (request: LegacyRequest) => - securityPlugin.authc.getCurrentUser(KibanaRequest.from(request)), - }); - }, + .default(), + init() {}, }); diff --git a/x-pack/plugins/apm/readme.md b/x-pack/plugins/apm/readme.md index ceed5e6c39716a..cb694712d7c97b 100644 --- a/x-pack/plugins/apm/readme.md +++ b/x-pack/plugins/apm/readme.md @@ -83,13 +83,13 @@ For debugging access Elasticsearch on http://localhost:9220` (elastic/changeme) **Start server** ``` -node scripts/functional_tests_server --config x-pack/test/api_integration/config.js +node scripts/functional_tests_server --config x-pack/test/api_integration/config.ts ``` **Run tests** ``` -node scripts/functional_test_runner --config x-pack/test/api_integration/config.js --grep='APM specs' +node scripts/functional_test_runner --config x-pack/test/api_integration/config.ts --grep='APM specs' ``` APM tests are located in `x-pack/test/api_integration/apis/apm`. diff --git a/x-pack/plugins/ingest_manager/README.md b/x-pack/plugins/ingest_manager/README.md index f0c2466b25b047..50c42544b8bdc7 100644 --- a/x-pack/plugins/ingest_manager/README.md +++ b/x-pack/plugins/ingest_manager/README.md @@ -52,12 +52,12 @@ This plugin follows the `common`, `server`, `public` structure from the [Archite 1. In one terminal, change to the `x-pack` directory and start the test server with ``` - node scripts/functional_tests_server.js --config test/api_integration/config.js + node scripts/functional_tests_server.js --config test/api_integration/config.ts ``` 1. in a second terminal, run the tests from the Kibana root directory with ``` - node scripts/functional_test_runner.js --config x-pack/test/api_integration/config.js + node scripts/functional_test_runner.js --config x-pack/test/api_integration/config.ts ``` #### EPM diff --git a/x-pack/plugins/lens/readme.md b/x-pack/plugins/lens/readme.md index 60b4266edadb3d..70d7f16b0f7fc9 100644 --- a/x-pack/plugins/lens/readme.md +++ b/x-pack/plugins/lens/readme.md @@ -11,4 +11,4 @@ Run all tests from the `x-pack` root directory - You may want to comment out all imports except for Lens in the config file. - API Functional tests: - Run `node scripts/functional_tests_server` - - Run `node ../scripts/functional_test_runner.js --config ./test/api_integration/config.js --grep=Lens` + - Run `node ../scripts/functional_test_runner.js --config ./test/api_integration/config.ts --grep=Lens` diff --git a/x-pack/plugins/security/public/account_management/account_management_app.ts b/x-pack/plugins/security/public/account_management/account_management_app.ts index 41567a04fe0302..0bb7785259c0ea 100644 --- a/x-pack/plugins/security/public/account_management/account_management_app.ts +++ b/x-pack/plugins/security/public/account_management/account_management_app.ts @@ -5,7 +5,12 @@ */ import { i18n } from '@kbn/i18n'; -import { StartServicesAccessor, ApplicationSetup, AppMountParameters } from 'src/core/public'; +import { + ApplicationSetup, + AppMountParameters, + AppNavLinkStatus, + StartServicesAccessor, +} from '../../../../../src/core/public'; import { AuthenticationServiceSetup } from '../authentication'; interface CreateDeps { @@ -23,8 +28,7 @@ export const accountManagementApp = Object.freeze({ application.register({ id: this.id, title, - // TODO: switch to proper enum once https://github.com/elastic/kibana/issues/58327 is resolved. - navLinkStatus: 3, + navLinkStatus: AppNavLinkStatus.hidden, appRoute: '/security/account', async mount({ element }: AppMountParameters) { const [ diff --git a/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.scss b/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.scss index 6784052ef4337e..344cde9c7825ce 100644 --- a/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.scss +++ b/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.scss @@ -23,9 +23,10 @@ } &:focus { + @include euiFocusRing; + border-color: transparent; border-radius: $euiBorderRadius; - @include euiFocusRing; .secLoginCard__title { text-decoration: underline; diff --git a/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_privileges.ts b/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_privileges.ts index 98110a83103aa0..6821c163d817dd 100644 --- a/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_privileges.ts +++ b/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_privileges.ts @@ -11,13 +11,15 @@ import { Feature } from '../../../../../features/public'; import { KibanaPrivileges } from '../model'; import { SecurityLicenseFeatures } from '../../..'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { featuresPluginMock } from '../../../../../features/server/mocks'; + export const createRawKibanaPrivileges = ( features: Feature[], { allowSubFeaturePrivileges = true } = {} ) => { - const featuresService = { - getFeatures: () => features, - }; + const featuresService = featuresPluginMock.createSetup(); + featuresService.getFeatures.mockReturnValue(features); const licensingService = { getFeatures: () => ({ allowSubFeaturePrivileges } as SecurityLicenseFeatures), diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx index afb8b6ec5dbe0c..43387d913e6fc5 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx @@ -163,7 +163,12 @@ function getProps({ const { http, docLinks, notifications } = coreMock.createStart(); http.get.mockImplementation(async (path: any) => { if (path === '/api/spaces/space') { - return buildSpaces(); + if (spacesEnabled) { + return buildSpaces(); + } + + const notFoundError = { response: { status: 404 } }; + throw notFoundError; } }); @@ -181,7 +186,6 @@ function getProps({ notifications, docLinks: new DocumentationLinksService(docLinks), fatalErrors, - spacesEnabled, uiCapabilities: buildUICapabilities(canManageSpaces), history: (scopedHistoryMock.create() as unknown) as ScopedHistory, }; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index 77f4455d813c6b..15888733ec4247 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -80,7 +80,6 @@ interface Props { docLinks: DocumentationLinksService; http: HttpStart; license: SecurityLicense; - spacesEnabled: boolean; uiCapabilities: Capabilities; notifications: NotificationsStart; fatalErrors: FatalErrorsSetup; @@ -225,14 +224,21 @@ function useRole( return [role, setRole] as [Role | null, typeof setRole]; } -function useSpaces(http: HttpStart, fatalErrors: FatalErrorsSetup, spacesEnabled: boolean) { - const [spaces, setSpaces] = useState(null); +function useSpaces(http: HttpStart, fatalErrors: FatalErrorsSetup) { + const [spaces, setSpaces] = useState<{ enabled: boolean; list: Space[] } | null>(null); useEffect(() => { - (spacesEnabled ? http.get('/api/spaces/space') : Promise.resolve([])).then( - (fetchedSpaces) => setSpaces(fetchedSpaces), - (err) => fatalErrors.add(err) + http.get('/api/spaces/space').then( + (fetchedSpaces) => setSpaces({ enabled: true, list: fetchedSpaces }), + (err: IHttpFetchError) => { + // Spaces plugin can be disabled and hence this endpoint can be unavailable. + if (err.response?.status === 404) { + setSpaces({ enabled: false, list: [] }); + } else { + fatalErrors.add(err); + } + } ); - }, [http, fatalErrors, spacesEnabled]); + }, [http, fatalErrors]); return spaces; } @@ -278,7 +284,6 @@ export const EditRolePage: FunctionComponent = ({ roleName, action, fatalErrors, - spacesEnabled, license, docLinks, uiCapabilities, @@ -295,7 +300,7 @@ export const EditRolePage: FunctionComponent = ({ const runAsUsers = useRunAsUsers(userAPIClient, fatalErrors); const indexPatternsTitles = useIndexPatternsTitles(indexPatterns, fatalErrors, notifications); const privileges = usePrivileges(privilegesAPIClient, fatalErrors); - const spaces = useSpaces(http, fatalErrors, spacesEnabled); + const spaces = useSpaces(http, fatalErrors); const features = useFeatures(getFeatures, fatalErrors); const [role, setRole] = useRole( rolesAPIClient, @@ -434,8 +439,8 @@ export const EditRolePage: FunctionComponent = ({ = ({ setFormError(null); try { - await rolesAPIClient.saveRole({ role, spacesEnabled }); + await rolesAPIClient.saveRole({ role, spacesEnabled: spaces.enabled }); } catch (error) { notifications.toasts.addDanger(get(error, 'data.message')); return; @@ -554,7 +559,7 @@ export const EditRolePage: FunctionComponent = ({ backToRoleList(); }; - const description = spacesEnabled ? ( + const description = spaces.enabled ? ( { diff --git a/x-pack/plugins/security/server/authorization/app_authorization.ts b/x-pack/plugins/security/server/authorization/app_authorization.ts index aead8cb07897c4..1036997ca821d1 100644 --- a/x-pack/plugins/security/server/authorization/app_authorization.ts +++ b/x-pack/plugins/security/server/authorization/app_authorization.ts @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CoreSetup, Logger } from '../../../../../src/core/server'; -import { FeaturesService } from '../plugin'; -import { Authorization } from '.'; +import { HttpServiceSetup, Logger } from '../../../../../src/core/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../../features/server'; +import { AuthorizationServiceSetup } from '.'; class ProtectedApplications { private applications: Set | null = null; - constructor(private readonly featuresService: FeaturesService) {} + constructor(private readonly featuresService: FeaturesPluginSetup) {} public shouldProtect(appId: string) { // Currently, once we get the list of features we essentially "lock" additional @@ -30,14 +30,14 @@ class ProtectedApplications { } export function initAppAuthorization( - http: CoreSetup['http'], + http: HttpServiceSetup, { actions, checkPrivilegesDynamicallyWithRequest, mode, - }: Pick, + }: Pick, logger: Logger, - featuresService: FeaturesService + featuresService: FeaturesPluginSetup ) { const protectedApplications = new ProtectedApplications(featuresService); diff --git a/x-pack/plugins/security/server/authorization/authorization_service.test.ts b/x-pack/plugins/security/server/authorization/authorization_service.test.ts new file mode 100644 index 00000000000000..978c985cfe820d --- /dev/null +++ b/x-pack/plugins/security/server/authorization/authorization_service.test.ts @@ -0,0 +1,263 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + mockAuthorizationModeFactory, + mockCheckPrivilegesDynamicallyWithRequestFactory, + mockCheckPrivilegesWithRequestFactory, + mockCheckSavedObjectsPrivilegesWithRequestFactory, + mockPrivilegesFactory, + mockRegisterPrivilegesWithCluster, +} from './service.test.mocks'; + +import { BehaviorSubject } from 'rxjs'; +import { CoreStatus, ServiceStatusLevels } from '../../../../../src/core/server'; +import { checkPrivilegesWithRequestFactory } from './check_privileges'; +import { checkPrivilegesDynamicallyWithRequestFactory } from './check_privileges_dynamically'; +import { checkSavedObjectsPrivilegesWithRequestFactory } from './check_saved_objects_privileges'; +import { authorizationModeFactory } from './mode'; +import { privilegesFactory } from './privileges'; +import { AuthorizationService } from '.'; + +import { + coreMock, + elasticsearchServiceMock, + loggingServiceMock, +} from '../../../../../src/core/server/mocks'; +import { featuresPluginMock } from '../../../features/server/mocks'; +import { licenseMock } from '../../common/licensing/index.mock'; +import { SecurityLicense, SecurityLicenseFeatures } from '../../common/licensing'; +import { nextTick } from 'test_utils/enzyme_helpers'; + +const kibanaIndexName = '.a-kibana-index'; +const application = `kibana-${kibanaIndexName}`; +const mockCheckPrivilegesWithRequest = Symbol(); +const mockCheckPrivilegesDynamicallyWithRequest = Symbol(); +const mockCheckSavedObjectsPrivilegesWithRequest = Symbol(); +const mockPrivilegesService = Symbol(); +const mockAuthorizationMode = Symbol(); +beforeEach(() => { + mockCheckPrivilegesWithRequestFactory.mockReturnValue(mockCheckPrivilegesWithRequest); + mockCheckPrivilegesDynamicallyWithRequestFactory.mockReturnValue( + mockCheckPrivilegesDynamicallyWithRequest + ); + mockCheckSavedObjectsPrivilegesWithRequestFactory.mockReturnValue( + mockCheckSavedObjectsPrivilegesWithRequest + ); + mockPrivilegesFactory.mockReturnValue(mockPrivilegesService); + mockAuthorizationModeFactory.mockReturnValue(mockAuthorizationMode); +}); + +afterEach(() => { + mockRegisterPrivilegesWithCluster.mockClear(); +}); + +it(`#setup returns exposed services`, () => { + const mockClusterClient = elasticsearchServiceMock.createClusterClient(); + const mockGetSpacesService = jest + .fn() + .mockReturnValue({ getSpaceId: jest.fn(), namespaceToSpaceId: jest.fn() }); + const mockFeaturesSetup = featuresPluginMock.createSetup(); + const mockLicense = licenseMock.create(); + const mockCoreSetup = coreMock.createSetup(); + + const authorizationService = new AuthorizationService(); + const authz = authorizationService.setup({ + http: mockCoreSetup.http, + capabilities: mockCoreSetup.capabilities, + status: mockCoreSetup.status, + clusterClient: mockClusterClient, + license: mockLicense, + loggers: loggingServiceMock.create(), + kibanaIndexName, + packageVersion: 'some-version', + features: mockFeaturesSetup, + getSpacesService: mockGetSpacesService, + }); + + expect(authz.actions.version).toBe('version:some-version'); + expect(authz.applicationName).toBe(application); + + expect(authz.checkPrivilegesWithRequest).toBe(mockCheckPrivilegesWithRequest); + expect(checkPrivilegesWithRequestFactory).toHaveBeenCalledWith( + authz.actions, + mockClusterClient, + authz.applicationName + ); + + expect(authz.checkPrivilegesDynamicallyWithRequest).toBe( + mockCheckPrivilegesDynamicallyWithRequest + ); + expect(checkPrivilegesDynamicallyWithRequestFactory).toHaveBeenCalledWith( + mockCheckPrivilegesWithRequest, + mockGetSpacesService + ); + + expect(authz.checkSavedObjectsPrivilegesWithRequest).toBe( + mockCheckSavedObjectsPrivilegesWithRequest + ); + expect(checkSavedObjectsPrivilegesWithRequestFactory).toHaveBeenCalledWith( + mockCheckPrivilegesWithRequest, + mockGetSpacesService + ); + + expect(authz.privileges).toBe(mockPrivilegesService); + expect(privilegesFactory).toHaveBeenCalledWith(authz.actions, mockFeaturesSetup, mockLicense); + + expect(authz.mode).toBe(mockAuthorizationMode); + expect(authorizationModeFactory).toHaveBeenCalledWith(mockLicense); + + expect(mockCoreSetup.capabilities.registerSwitcher).toHaveBeenCalledTimes(1); + expect(mockCoreSetup.capabilities.registerSwitcher).toHaveBeenCalledWith(expect.any(Function)); +}); + +describe('#start', () => { + let statusSubject: BehaviorSubject; + let licenseSubject: BehaviorSubject; + let mockLicense: jest.Mocked; + beforeEach(() => { + const mockClusterClient = elasticsearchServiceMock.createClusterClient(); + + licenseSubject = new BehaviorSubject(({} as unknown) as SecurityLicenseFeatures); + mockLicense = licenseMock.create(); + mockLicense.isEnabled.mockReturnValue(false); + mockLicense.features$ = licenseSubject; + + statusSubject = new BehaviorSubject({ + elasticsearch: { level: ServiceStatusLevels.unavailable, summary: 'Service is NOT working' }, + savedObjects: { level: ServiceStatusLevels.unavailable, summary: 'Service is NOT working' }, + }); + const mockCoreSetup = coreMock.createSetup(); + mockCoreSetup.status.core$ = statusSubject; + + const authorizationService = new AuthorizationService(); + authorizationService.setup({ + http: mockCoreSetup.http, + capabilities: mockCoreSetup.capabilities, + status: mockCoreSetup.status, + clusterClient: mockClusterClient, + license: mockLicense, + loggers: loggingServiceMock.create(), + kibanaIndexName, + packageVersion: 'some-version', + features: featuresPluginMock.createSetup(), + getSpacesService: jest + .fn() + .mockReturnValue({ getSpaceId: jest.fn(), namespaceToSpaceId: jest.fn() }), + }); + + const featuresStart = featuresPluginMock.createStart(); + featuresStart.getFeatures.mockReturnValue([]); + + authorizationService.start({ clusterClient: mockClusterClient, features: featuresStart }); + + // ES and license aren't available yet. + expect(mockRegisterPrivilegesWithCluster).not.toHaveBeenCalled(); + }); + + it('registers cluster privileges', async () => { + // ES is available now, but not license. + statusSubject.next({ + elasticsearch: { level: ServiceStatusLevels.available, summary: 'Service is working' }, + savedObjects: { level: ServiceStatusLevels.unavailable, summary: 'Service is NOT working' }, + }); + expect(mockRegisterPrivilegesWithCluster).not.toHaveBeenCalled(); + + // Both ES and license are available now. + mockLicense.isEnabled.mockReturnValue(true); + licenseSubject.next(({} as unknown) as SecurityLicenseFeatures); + expect(mockRegisterPrivilegesWithCluster).toHaveBeenCalledTimes(1); + + await nextTick(); + + // New changes still trigger privileges re-registration. + licenseSubject.next(({} as unknown) as SecurityLicenseFeatures); + expect(mockRegisterPrivilegesWithCluster).toHaveBeenCalledTimes(2); + }); + + it('schedules retries if fails to register cluster privileges', async () => { + jest.useFakeTimers(); + + mockRegisterPrivilegesWithCluster.mockRejectedValue(new Error('Some error')); + + // Both ES and license are available. + mockLicense.isEnabled.mockReturnValue(true); + statusSubject.next({ + elasticsearch: { level: ServiceStatusLevels.available, summary: 'Service is working' }, + savedObjects: { level: ServiceStatusLevels.unavailable, summary: 'Service is NOT working' }, + }); + expect(mockRegisterPrivilegesWithCluster).toHaveBeenCalledTimes(1); + + // Next retry isn't performed immediately, retry happens only after a timeout. + await nextTick(); + expect(mockRegisterPrivilegesWithCluster).toHaveBeenCalledTimes(1); + jest.advanceTimersByTime(100); + expect(mockRegisterPrivilegesWithCluster).toHaveBeenCalledTimes(2); + + // Delay between consequent retries is increasing. + await nextTick(); + jest.advanceTimersByTime(100); + expect(mockRegisterPrivilegesWithCluster).toHaveBeenCalledTimes(2); + await nextTick(); + jest.advanceTimersByTime(100); + expect(mockRegisterPrivilegesWithCluster).toHaveBeenCalledTimes(3); + + // When call finally succeeds retries aren't scheduled anymore. + mockRegisterPrivilegesWithCluster.mockResolvedValue(undefined); + await nextTick(); + jest.runAllTimers(); + expect(mockRegisterPrivilegesWithCluster).toHaveBeenCalledTimes(4); + await nextTick(); + jest.runAllTimers(); + expect(mockRegisterPrivilegesWithCluster).toHaveBeenCalledTimes(4); + + // New changes still trigger privileges re-registration. + licenseSubject.next(({} as unknown) as SecurityLicenseFeatures); + expect(mockRegisterPrivilegesWithCluster).toHaveBeenCalledTimes(5); + }); +}); + +it('#stop unsubscribes from license and ES updates.', () => { + const mockClusterClient = elasticsearchServiceMock.createClusterClient(); + + const licenseSubject = new BehaviorSubject(({} as unknown) as SecurityLicenseFeatures); + const mockLicense = licenseMock.create(); + mockLicense.isEnabled.mockReturnValue(false); + mockLicense.features$ = licenseSubject; + + const mockCoreSetup = coreMock.createSetup(); + mockCoreSetup.status.core$ = new BehaviorSubject({ + elasticsearch: { level: ServiceStatusLevels.available, summary: 'Service is working' }, + savedObjects: { level: ServiceStatusLevels.available, summary: 'Service is working' }, + }); + + const authorizationService = new AuthorizationService(); + authorizationService.setup({ + http: mockCoreSetup.http, + capabilities: mockCoreSetup.capabilities, + status: mockCoreSetup.status, + clusterClient: mockClusterClient, + license: mockLicense, + loggers: loggingServiceMock.create(), + kibanaIndexName, + packageVersion: 'some-version', + features: featuresPluginMock.createSetup(), + getSpacesService: jest + .fn() + .mockReturnValue({ getSpaceId: jest.fn(), namespaceToSpaceId: jest.fn() }), + }); + + const featuresStart = featuresPluginMock.createStart(); + featuresStart.getFeatures.mockReturnValue([]); + authorizationService.start({ clusterClient: mockClusterClient, features: featuresStart }); + + authorizationService.stop(); + + // After stop we don't register privileges even if all requirements are met. + mockLicense.isEnabled.mockReturnValue(true); + licenseSubject.next(({} as unknown) as SecurityLicenseFeatures); + expect(mockRegisterPrivilegesWithCluster).not.toHaveBeenCalled(); +}); diff --git a/x-pack/plugins/security/server/authorization/authorization_service.ts b/x-pack/plugins/security/server/authorization/authorization_service.ts new file mode 100644 index 00000000000000..989784a1436d26 --- /dev/null +++ b/x-pack/plugins/security/server/authorization/authorization_service.ts @@ -0,0 +1,221 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { combineLatest, BehaviorSubject, Subscription } from 'rxjs'; +import { distinctUntilChanged, filter } from 'rxjs/operators'; +import { UICapabilities } from 'ui/capabilities'; +import { + LoggerFactory, + KibanaRequest, + IClusterClient, + ServiceStatusLevels, + Logger, + StatusServiceSetup, + HttpServiceSetup, + CapabilitiesSetup, +} from '../../../../../src/core/server'; + +import { + PluginSetupContract as FeaturesPluginSetup, + PluginStartContract as FeaturesPluginStart, +} from '../../../features/server'; + +import { SpacesService } from '../plugin'; +import { Actions } from './actions'; +import { CheckPrivilegesWithRequest, checkPrivilegesWithRequestFactory } from './check_privileges'; +import { + CheckPrivilegesDynamicallyWithRequest, + checkPrivilegesDynamicallyWithRequestFactory, +} from './check_privileges_dynamically'; +import { + CheckSavedObjectsPrivilegesWithRequest, + checkSavedObjectsPrivilegesWithRequestFactory, +} from './check_saved_objects_privileges'; +import { AuthorizationMode, authorizationModeFactory } from './mode'; +import { privilegesFactory, PrivilegesService } from './privileges'; +import { initAppAuthorization } from './app_authorization'; +import { initAPIAuthorization } from './api_authorization'; +import { disableUICapabilitiesFactory } from './disable_ui_capabilities'; +import { validateFeaturePrivileges } from './validate_feature_privileges'; +import { validateReservedPrivileges } from './validate_reserved_privileges'; +import { registerPrivilegesWithCluster } from './register_privileges_with_cluster'; +import { APPLICATION_PREFIX } from '../../common/constants'; +import { SecurityLicense } from '../../common/licensing'; + +export { Actions } from './actions'; +export { CheckSavedObjectsPrivileges } from './check_saved_objects_privileges'; +export { featurePrivilegeIterator } from './privileges'; + +interface AuthorizationServiceSetupParams { + packageVersion: string; + http: HttpServiceSetup; + status: StatusServiceSetup; + capabilities: CapabilitiesSetup; + clusterClient: IClusterClient; + license: SecurityLicense; + loggers: LoggerFactory; + features: FeaturesPluginSetup; + kibanaIndexName: string; + getSpacesService(): SpacesService | undefined; +} + +interface AuthorizationServiceStartParams { + features: FeaturesPluginStart; + clusterClient: IClusterClient; +} + +export interface AuthorizationServiceSetup { + actions: Actions; + checkPrivilegesWithRequest: CheckPrivilegesWithRequest; + checkPrivilegesDynamicallyWithRequest: CheckPrivilegesDynamicallyWithRequest; + checkSavedObjectsPrivilegesWithRequest: CheckSavedObjectsPrivilegesWithRequest; + applicationName: string; + mode: AuthorizationMode; + privileges: PrivilegesService; +} + +export class AuthorizationService { + private logger!: Logger; + private license!: SecurityLicense; + private status!: StatusServiceSetup; + private applicationName!: string; + private privileges!: PrivilegesService; + + private statusSubscription?: Subscription; + + setup({ + http, + capabilities, + status, + packageVersion, + clusterClient, + license, + loggers, + features, + kibanaIndexName, + getSpacesService, + }: AuthorizationServiceSetupParams): AuthorizationServiceSetup { + this.logger = loggers.get('authorization'); + this.license = license; + this.status = status; + this.applicationName = `${APPLICATION_PREFIX}${kibanaIndexName}`; + + const mode = authorizationModeFactory(license); + const actions = new Actions(packageVersion); + this.privileges = privilegesFactory(actions, features, license); + + const checkPrivilegesWithRequest = checkPrivilegesWithRequestFactory( + actions, + clusterClient, + this.applicationName + ); + + const authz = { + actions, + applicationName: this.applicationName, + mode, + privileges: this.privileges, + checkPrivilegesWithRequest, + checkPrivilegesDynamicallyWithRequest: checkPrivilegesDynamicallyWithRequestFactory( + checkPrivilegesWithRequest, + getSpacesService + ), + checkSavedObjectsPrivilegesWithRequest: checkSavedObjectsPrivilegesWithRequestFactory( + checkPrivilegesWithRequest, + getSpacesService + ), + }; + + capabilities.registerSwitcher( + async (request: KibanaRequest, uiCapabilities: UICapabilities) => { + // If we have a license which doesn't enable security, or we're a legacy user we shouldn't + // disable any ui capabilities + if (!mode.useRbacForRequest(request)) { + return uiCapabilities; + } + + const disableUICapabilities = disableUICapabilitiesFactory( + request, + features.getFeatures(), + this.logger, + authz + ); + + if (!request.auth.isAuthenticated) { + return disableUICapabilities.all(uiCapabilities); + } + + return await disableUICapabilities.usingPrivileges(uiCapabilities); + } + ); + + initAPIAuthorization(http, authz, loggers.get('api-authorization')); + initAppAuthorization(http, authz, loggers.get('app-authorization'), features); + + return authz; + } + + start({ clusterClient, features }: AuthorizationServiceStartParams) { + const allFeatures = features.getFeatures(); + validateFeaturePrivileges(allFeatures); + validateReservedPrivileges(allFeatures); + + this.registerPrivileges(clusterClient); + } + + stop() { + if (this.statusSubscription !== undefined) { + this.statusSubscription.unsubscribe(); + this.statusSubscription = undefined; + } + } + + private registerPrivileges(clusterClient: IClusterClient) { + const RETRY_SCALE_DURATION = 100; + const RETRY_TIMEOUT_MAX = 10000; + const retries$ = new BehaviorSubject(0); + let retryTimeout: NodeJS.Timeout; + + // Register cluster privileges once Elasticsearch is available and Security plugin is enabled. + this.statusSubscription = combineLatest([ + this.status.core$, + this.license.features$, + retries$.asObservable().pipe( + // We shouldn't emit new value if retry counter is reset. This comparator isn't called for + // the initial value. + distinctUntilChanged((prev, curr) => prev === curr || curr === 0) + ), + ]) + .pipe( + filter( + ([status]) => + this.license.isEnabled() && status.elasticsearch.level === ServiceStatusLevels.available + ) + ) + .subscribe(async () => { + // If status or license change occurred before retry timeout we should cancel it. + if (retryTimeout) { + clearTimeout(retryTimeout); + } + + try { + await registerPrivilegesWithCluster( + this.logger, + this.privileges, + this.applicationName, + clusterClient + ); + retries$.next(0); + } catch (err) { + const retriesElapsed = retries$.getValue() + 1; + retryTimeout = setTimeout( + () => retries$.next(retriesElapsed), + Math.min(retriesElapsed * RETRY_SCALE_DURATION, RETRY_TIMEOUT_MAX) + ); + } + }); + } +} diff --git a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts index 72937c15756ac9..183ad9169a1233 100644 --- a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts +++ b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts @@ -10,13 +10,13 @@ import { KibanaRequest, Logger } from '../../../../../src/core/server'; import { Feature } from '../../../features/server'; import { CheckPrivilegesResponse } from './check_privileges'; -import { Authorization } from './index'; +import { AuthorizationServiceSetup } from '.'; export function disableUICapabilitiesFactory( request: KibanaRequest, features: Feature[], logger: Logger, - authz: Authorization + authz: AuthorizationServiceSetup ) { const featureNavLinkIds = features .map((feature) => feature.navLinkId) diff --git a/x-pack/plugins/security/server/authorization/index.test.ts b/x-pack/plugins/security/server/authorization/index.test.ts deleted file mode 100644 index 32520534547649..00000000000000 --- a/x-pack/plugins/security/server/authorization/index.test.ts +++ /dev/null @@ -1,100 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - mockAuthorizationModeFactory, - mockCheckPrivilegesDynamicallyWithRequestFactory, - mockCheckPrivilegesWithRequestFactory, - mockCheckSavedObjectsPrivilegesWithRequestFactory, - mockPrivilegesFactory, -} from './service.test.mocks'; - -import { checkPrivilegesWithRequestFactory } from './check_privileges'; -import { checkPrivilegesDynamicallyWithRequestFactory } from './check_privileges_dynamically'; -import { checkSavedObjectsPrivilegesWithRequestFactory } from './check_saved_objects_privileges'; -import { authorizationModeFactory } from './mode'; -import { privilegesFactory } from './privileges'; -import { setupAuthorization } from '.'; - -import { - coreMock, - elasticsearchServiceMock, - loggingServiceMock, -} from '../../../../../src/core/server/mocks'; -import { licenseMock } from '../../common/licensing/index.mock'; - -test(`returns exposed services`, () => { - const kibanaIndexName = '.a-kibana-index'; - const application = `kibana-${kibanaIndexName}`; - - const mockCheckPrivilegesWithRequest = Symbol(); - mockCheckPrivilegesWithRequestFactory.mockReturnValue(mockCheckPrivilegesWithRequest); - - const mockCheckPrivilegesDynamicallyWithRequest = Symbol(); - mockCheckPrivilegesDynamicallyWithRequestFactory.mockReturnValue( - mockCheckPrivilegesDynamicallyWithRequest - ); - - const mockCheckSavedObjectsPrivilegesWithRequest = Symbol(); - mockCheckSavedObjectsPrivilegesWithRequestFactory.mockReturnValue( - mockCheckSavedObjectsPrivilegesWithRequest - ); - - const mockPrivilegesService = Symbol(); - mockPrivilegesFactory.mockReturnValue(mockPrivilegesService); - const mockAuthorizationMode = Symbol(); - mockAuthorizationModeFactory.mockReturnValue(mockAuthorizationMode); - - const mockClusterClient = elasticsearchServiceMock.createClusterClient(); - const mockGetSpacesService = jest - .fn() - .mockReturnValue({ getSpaceId: jest.fn(), namespaceToSpaceId: jest.fn() }); - const mockFeaturesService = { getFeatures: () => [] }; - const mockLicense = licenseMock.create(); - - const authz = setupAuthorization({ - http: coreMock.createSetup().http, - clusterClient: mockClusterClient, - license: mockLicense, - loggers: loggingServiceMock.create(), - kibanaIndexName, - packageVersion: 'some-version', - featuresService: mockFeaturesService, - getSpacesService: mockGetSpacesService, - }); - - expect(authz.actions.version).toBe('version:some-version'); - expect(authz.applicationName).toBe(application); - - expect(authz.checkPrivilegesWithRequest).toBe(mockCheckPrivilegesWithRequest); - expect(checkPrivilegesWithRequestFactory).toHaveBeenCalledWith( - authz.actions, - mockClusterClient, - authz.applicationName - ); - - expect(authz.checkPrivilegesDynamicallyWithRequest).toBe( - mockCheckPrivilegesDynamicallyWithRequest - ); - expect(checkPrivilegesDynamicallyWithRequestFactory).toHaveBeenCalledWith( - mockCheckPrivilegesWithRequest, - mockGetSpacesService - ); - - expect(authz.checkSavedObjectsPrivilegesWithRequest).toBe( - mockCheckSavedObjectsPrivilegesWithRequest - ); - expect(checkSavedObjectsPrivilegesWithRequestFactory).toHaveBeenCalledWith( - mockCheckPrivilegesWithRequest, - mockGetSpacesService - ); - - expect(authz.privileges).toBe(mockPrivilegesService); - expect(privilegesFactory).toHaveBeenCalledWith(authz.actions, mockFeaturesService, mockLicense); - - expect(authz.mode).toBe(mockAuthorizationMode); - expect(authorizationModeFactory).toHaveBeenCalledWith(mockLicense); -}); diff --git a/x-pack/plugins/security/server/authorization/index.ts b/x-pack/plugins/security/server/authorization/index.ts index cf970a561b93f9..d5c1323354f86e 100644 --- a/x-pack/plugins/security/server/authorization/index.ts +++ b/x-pack/plugins/security/server/authorization/index.ts @@ -4,134 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { UICapabilities } from 'ui/capabilities'; -import { - CoreSetup, - LoggerFactory, - KibanaRequest, - IClusterClient, -} from '../../../../../src/core/server'; - -import { FeaturesService, SpacesService } from '../plugin'; -import { Actions } from './actions'; -import { CheckPrivilegesWithRequest, checkPrivilegesWithRequestFactory } from './check_privileges'; -import { - CheckPrivilegesDynamicallyWithRequest, - checkPrivilegesDynamicallyWithRequestFactory, -} from './check_privileges_dynamically'; -import { - CheckSavedObjectsPrivilegesWithRequest, - checkSavedObjectsPrivilegesWithRequestFactory, -} from './check_saved_objects_privileges'; -import { AuthorizationMode, authorizationModeFactory } from './mode'; -import { privilegesFactory, PrivilegesService } from './privileges'; -import { initAppAuthorization } from './app_authorization'; -import { initAPIAuthorization } from './api_authorization'; -import { disableUICapabilitiesFactory } from './disable_ui_capabilities'; -import { validateFeaturePrivileges } from './validate_feature_privileges'; -import { validateReservedPrivileges } from './validate_reserved_privileges'; -import { registerPrivilegesWithCluster } from './register_privileges_with_cluster'; -import { APPLICATION_PREFIX } from '../../common/constants'; -import { SecurityLicense } from '../../common/licensing'; - export { Actions } from './actions'; +export { AuthorizationService, AuthorizationServiceSetup } from './authorization_service'; export { CheckSavedObjectsPrivileges } from './check_saved_objects_privileges'; export { featurePrivilegeIterator } from './privileges'; - -interface SetupAuthorizationParams { - packageVersion: string; - http: CoreSetup['http']; - clusterClient: IClusterClient; - license: SecurityLicense; - loggers: LoggerFactory; - featuresService: FeaturesService; - kibanaIndexName: string; - getSpacesService(): SpacesService | undefined; -} - -export interface Authorization { - actions: Actions; - checkPrivilegesWithRequest: CheckPrivilegesWithRequest; - checkPrivilegesDynamicallyWithRequest: CheckPrivilegesDynamicallyWithRequest; - checkSavedObjectsPrivilegesWithRequest: CheckSavedObjectsPrivilegesWithRequest; - applicationName: string; - mode: AuthorizationMode; - privileges: PrivilegesService; - disableUnauthorizedCapabilities: ( - request: KibanaRequest, - capabilities: UICapabilities - ) => Promise; - registerPrivilegesWithCluster: () => Promise; -} - -export function setupAuthorization({ - http, - packageVersion, - clusterClient, - license, - loggers, - featuresService, - kibanaIndexName, - getSpacesService, -}: SetupAuthorizationParams): Authorization { - const actions = new Actions(packageVersion); - const mode = authorizationModeFactory(license); - const applicationName = `${APPLICATION_PREFIX}${kibanaIndexName}`; - const checkPrivilegesWithRequest = checkPrivilegesWithRequestFactory( - actions, - clusterClient, - applicationName - ); - const privileges = privilegesFactory(actions, featuresService, license); - const logger = loggers.get('authorization'); - - const authz = { - actions, - applicationName, - checkPrivilegesWithRequest, - checkPrivilegesDynamicallyWithRequest: checkPrivilegesDynamicallyWithRequestFactory( - checkPrivilegesWithRequest, - getSpacesService - ), - checkSavedObjectsPrivilegesWithRequest: checkSavedObjectsPrivilegesWithRequestFactory( - checkPrivilegesWithRequest, - getSpacesService - ), - mode, - privileges, - - async disableUnauthorizedCapabilities(request: KibanaRequest, capabilities: UICapabilities) { - // If we have a license which doesn't enable security, or we're a legacy user we shouldn't - // disable any ui capabilities - if (!mode.useRbacForRequest(request)) { - return capabilities; - } - - const disableUICapabilities = disableUICapabilitiesFactory( - request, - featuresService.getFeatures(), - logger, - authz - ); - - if (!request.auth.isAuthenticated) { - return disableUICapabilities.all(capabilities); - } - - return await disableUICapabilities.usingPrivileges(capabilities); - }, - - registerPrivilegesWithCluster: async () => { - const features = featuresService.getFeatures(); - validateFeaturePrivileges(features); - validateReservedPrivileges(features); - - await registerPrivilegesWithCluster(logger, privileges, applicationName, clusterClient); - }, - }; - - initAPIAuthorization(http, authz, loggers.get('api-authorization')); - initAppAuthorization(http, authz, loggers.get('app-authorization'), featuresService); - - return authz; -} diff --git a/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts b/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts index b023c12d35b79a..06f064a379fe6e 100644 --- a/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts @@ -8,6 +8,8 @@ import { Feature } from '../../../../features/server'; import { Actions } from '../actions'; import { privilegesFactory } from './privileges'; +import { featuresPluginMock } from '../../../../features/server/mocks'; + const actions = new Actions('1.0.0-zeta1'); describe('features', () => { @@ -42,7 +44,9 @@ describe('features', () => { }), ]; - const mockFeaturesService = { getFeatures: jest.fn().mockReturnValue(features) }; + const mockFeaturesService = featuresPluginMock.createSetup(); + mockFeaturesService.getFeatures.mockReturnValue(features); + const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; diff --git a/x-pack/plugins/security/server/authorization/privileges/privileges.ts b/x-pack/plugins/security/server/authorization/privileges/privileges.ts index f3b2881e79ece5..5a15290a7f1a29 100644 --- a/x-pack/plugins/security/server/authorization/privileges/privileges.ts +++ b/x-pack/plugins/security/server/authorization/privileges/privileges.ts @@ -6,11 +6,10 @@ import { uniq } from 'lodash'; import { SecurityLicense } from '../../../common/licensing'; -import { Feature } from '../../../../features/server'; +import { Feature, PluginSetupContract as FeaturesPluginSetup } from '../../../../features/server'; import { RawKibanaPrivileges } from '../../../common/model'; import { Actions } from '../actions'; import { featurePrivilegeBuilderFactory } from './feature_privilege_builder'; -import { FeaturesService } from '../../plugin'; import { featurePrivilegeIterator, subFeaturePrivilegeIterator, @@ -22,7 +21,7 @@ export interface PrivilegesService { export function privilegesFactory( actions: Actions, - featuresService: FeaturesService, + featuresService: FeaturesPluginSetup, licenseService: Pick ) { const featurePrivilegeBuilder = featurePrivilegeBuilderFactory(actions); diff --git a/x-pack/plugins/security/server/authorization/register_privileges_with_cluster.test.ts b/x-pack/plugins/security/server/authorization/register_privileges_with_cluster.test.ts index fff4345c72409f..e21203e60b887e 100644 --- a/x-pack/plugins/security/server/authorization/register_privileges_with_cluster.test.ts +++ b/x-pack/plugins/security/server/authorization/register_privileges_with_cluster.test.ts @@ -49,7 +49,7 @@ const registerPrivilegesWithClusterTest = ( }); for (const deletedPrivilege of deletedPrivileges) { expect(mockLogger.debug).toHaveBeenCalledWith( - `Deleting Kibana Privilege ${deletedPrivilege} from Elasticearch for ${application}` + `Deleting Kibana Privilege ${deletedPrivilege} from Elasticsearch for ${application}` ); expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( 'shield.deletePrivilege', @@ -82,7 +82,7 @@ const registerPrivilegesWithClusterTest = ( `Registering Kibana Privileges with Elasticsearch for ${application}` ); expect(mockLogger.debug).toHaveBeenCalledWith( - `Kibana Privileges already registered with Elasticearch for ${application}` + `Kibana Privileges already registered with Elasticsearch for ${application}` ); }; }; diff --git a/x-pack/plugins/security/server/authorization/register_privileges_with_cluster.ts b/x-pack/plugins/security/server/authorization/register_privileges_with_cluster.ts index 22e7830d20e28e..8e54794494a908 100644 --- a/x-pack/plugins/security/server/authorization/register_privileges_with_cluster.ts +++ b/x-pack/plugins/security/server/authorization/register_privileges_with_cluster.ts @@ -61,14 +61,14 @@ export async function registerPrivilegesWithCluster( privilege: application, }); if (arePrivilegesEqual(existingPrivileges, expectedPrivileges)) { - logger.debug(`Kibana Privileges already registered with Elasticearch for ${application}`); + logger.debug(`Kibana Privileges already registered with Elasticsearch for ${application}`); return; } const privilegesToDelete = getPrivilegesToDelete(existingPrivileges, expectedPrivileges); for (const privilegeToDelete of privilegesToDelete) { logger.debug( - `Deleting Kibana Privilege ${privilegeToDelete} from Elasticearch for ${application}` + `Deleting Kibana Privilege ${privilegeToDelete} from Elasticsearch for ${application}` ); try { await clusterClient.callAsInternalUser('shield.deletePrivilege', { diff --git a/x-pack/plugins/security/server/authorization/service.test.mocks.ts b/x-pack/plugins/security/server/authorization/service.test.mocks.ts index 5cd2eac20094dd..d73adde66a4907 100644 --- a/x-pack/plugins/security/server/authorization/service.test.mocks.ts +++ b/x-pack/plugins/security/server/authorization/service.test.mocks.ts @@ -28,3 +28,8 @@ export const mockAuthorizationModeFactory = jest.fn(); jest.mock('./mode', () => ({ authorizationModeFactory: mockAuthorizationModeFactory, })); + +export const mockRegisterPrivilegesWithCluster = jest.fn(); +jest.mock('./register_privileges_with_cluster', () => ({ + registerPrivilegesWithCluster: mockRegisterPrivilegesWithCluster, +})); diff --git a/x-pack/plugins/security/server/mocks.ts b/x-pack/plugins/security/server/mocks.ts index 72a946d6c51557..c2d99433b03466 100644 --- a/x-pack/plugins/security/server/mocks.ts +++ b/x-pack/plugins/security/server/mocks.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SecurityPluginSetup } from './plugin'; - import { authenticationMock } from './authentication/index.mock'; import { authorizationMock } from './authorization/index.mock'; import { licenseMock } from '../common/licensing/index.mock'; @@ -23,7 +21,6 @@ function createSetupMock() { }, registerSpacesService: jest.fn(), license: licenseMock.create(), - __legacyCompat: {} as SecurityPluginSetup['__legacyCompat'], }; } diff --git a/x-pack/plugins/security/server/plugin.test.ts b/x-pack/plugins/security/server/plugin.test.ts index b7fd2aeae7edcc..e01c608e5f3065 100644 --- a/x-pack/plugins/security/server/plugin.test.ts +++ b/x-pack/plugins/security/server/plugin.test.ts @@ -50,9 +50,6 @@ describe('Security Plugin', () => { it('exposes proper contract', async () => { await expect(plugin.setup(mockCoreSetup, mockDependencies)).resolves.toMatchInlineSnapshot(` Object { - "__legacyCompat": Object { - "registerPrivilegesWithCluster": [Function], - }, "audit": Object { "getLogger": [Function], }, diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index b961b4e2f9066a..c8f47aaae7b5d8 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -8,22 +8,22 @@ import { combineLatest } from 'rxjs'; import { first, map } from 'rxjs/operators'; import { TypeOf } from '@kbn/config-schema'; import { + deepFreeze, ICustomClusterClient, CoreSetup, + CoreStart, Logger, PluginInitializerContext, - CoreStart, } from '../../../../src/core/server'; -import { deepFreeze } from '../../../../src/core/server'; import { SpacesPluginSetup } from '../../spaces/server'; import { - PluginSetupContract as FeaturesSetupContract, - PluginStartContract as FeaturesStartContract, + PluginSetupContract as FeaturesPluginSetup, + PluginStartContract as FeaturesPluginStart, } from '../../features/server'; import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/server'; import { Authentication, setupAuthentication } from './authentication'; -import { Authorization, setupAuthorization } from './authorization'; +import { AuthorizationService, AuthorizationServiceSetup } from './authorization'; import { ConfigSchema, createConfig } from './config'; import { defineRoutes } from './routes'; import { SecurityLicenseService, SecurityLicense } from '../common/licensing'; @@ -37,8 +37,6 @@ export type SpacesService = Pick< 'getSpaceId' | 'namespaceToSpaceId' >; -export type FeaturesService = Pick; - /** * Describes public Security plugin contract returned at the `setup` stage. */ @@ -53,7 +51,7 @@ export interface SecurityPluginSetup { | 'grantAPIKeyAsInternalUser' | 'invalidateAPIKeyAsInternalUser' >; - authz: Pick; + authz: Pick; license: SecurityLicense; audit: Pick; @@ -66,19 +64,15 @@ export interface SecurityPluginSetup { * @param service Spaces service exposed by the Spaces plugin. */ registerSpacesService: (service: SpacesService) => void; - - __legacyCompat: { - registerPrivilegesWithCluster: () => void; - }; } export interface PluginSetupDependencies { - features: FeaturesService; + features: FeaturesPluginSetup; licensing: LicensingPluginSetup; } export interface PluginStartDependencies { - features: FeaturesStartContract; + features: FeaturesPluginStart; licensing: LicensingPluginStart; } @@ -101,6 +95,7 @@ export class Plugin { }; private readonly auditService = new AuditService(this.initializerContext.logger.get('audit')); + private readonly authorizationService = new AuthorizationService(); private readonly getSpacesService = () => { // Changing property value from Symbol to undefined denotes the fact that property was accessed. @@ -156,15 +151,17 @@ export class Plugin { loggers: this.initializerContext.logger, }); - const authz = await setupAuthorization({ + const authz = this.authorizationService.setup({ http: core.http, + capabilities: core.capabilities, + status: core.status, clusterClient: this.clusterClient, license, loggers: this.initializerContext.logger, kibanaIndexName: legacyConfig.kibana.index, packageVersion: this.initializerContext.env.packageInfo.version, getSpacesService: this.getSpacesService, - featuresService: features, + features, }); setupSavedObjects({ @@ -174,8 +171,6 @@ export class Plugin { getSpacesService: this.getSpacesService, }); - core.capabilities.registerSwitcher(authz.disableUnauthorizedCapabilities); - defineRoutes({ router: core.http.createRouter(), basePath: core.http.basePath, @@ -223,18 +218,15 @@ export class Plugin { this.spacesService = service; }, - - __legacyCompat: { - registerPrivilegesWithCluster: async () => await authz.registerPrivilegesWithCluster(), - }, }); } - public start(core: CoreStart, { licensing }: PluginStartDependencies) { + public start(core: CoreStart, { features, licensing }: PluginStartDependencies) { this.logger.debug('Starting plugin'); this.featureUsageServiceStart = this.featureUsageService.start({ featureUsage: licensing.featureUsage, }); + this.authorizationService.start({ features, clusterClient: this.clusterClient! }); } public stop() { @@ -254,6 +246,7 @@ export class Plugin { this.featureUsageServiceStart = undefined; } this.auditService.stop(); + this.authorizationService.stop(); } private wasSpacesServiceAccessed() { diff --git a/x-pack/plugins/security/server/routes/index.ts b/x-pack/plugins/security/server/routes/index.ts index 7ef7638fc027ef..5721a2699d15ce 100644 --- a/x-pack/plugins/security/server/routes/index.ts +++ b/x-pack/plugins/security/server/routes/index.ts @@ -14,7 +14,7 @@ import { } from '../../../../../src/core/server'; import { SecurityLicense } from '../../common/licensing'; import { Authentication } from '../authentication'; -import { Authorization } from '../authorization'; +import { AuthorizationServiceSetup } from '../authorization'; import { ConfigType } from '../config'; import { defineAuthenticationRoutes } from './authentication'; @@ -37,7 +37,7 @@ export interface RouteDefinitionParams { clusterClient: IClusterClient; config: ConfigType; authc: Authentication; - authz: Authorization; + authz: AuthorizationServiceSetup; license: SecurityLicense; getFeatures: () => Promise; getFeatureUsageService: () => SecurityFeatureUsageServiceStart; diff --git a/x-pack/plugins/security/server/saved_objects/index.ts b/x-pack/plugins/security/server/saved_objects/index.ts index 29fbe3af21b951..6acfd06a0309bd 100644 --- a/x-pack/plugins/security/server/saved_objects/index.ts +++ b/x-pack/plugins/security/server/saved_objects/index.ts @@ -11,13 +11,16 @@ import { SavedObjectsClient, } from '../../../../../src/core/server'; import { SecureSavedObjectsClientWrapper } from './secure_saved_objects_client_wrapper'; -import { Authorization } from '../authorization'; +import { AuthorizationServiceSetup } from '../authorization'; import { SecurityAuditLogger } from '../audit'; import { SpacesService } from '../plugin'; interface SetupSavedObjectsParams { auditLogger: SecurityAuditLogger; - authz: Pick; + authz: Pick< + AuthorizationServiceSetup, + 'mode' | 'actions' | 'checkSavedObjectsPrivilegesWithRequest' + >; savedObjects: CoreSetup['savedObjects']; getSpacesService(): SpacesService | undefined; } diff --git a/x-pack/plugins/uptime/README.md b/x-pack/plugins/uptime/README.md index 10c1fc0edcd00d..54bf48e8d3c862 100644 --- a/x-pack/plugins/uptime/README.md +++ b/x-pack/plugins/uptime/README.md @@ -55,7 +55,7 @@ In another shell, from **~kibana/x-pack**: #### API tests If instead you need to run API tests, start up the test server and then in another shell, from **~kibana/x-pack**: -`node ../scripts/functional_test_runner.js --config test/api_integration/config.js --grep="{TEST_NAME}"`. +`node ../scripts/functional_test_runner.js --config test/api_integration/config.ts --grep="{TEST_NAME}"`. You can update snapshots by prefixing the runner command with `env UPDATE_UPTIME_FIXTURES=1` diff --git a/x-pack/scripts/functional_tests.js b/x-pack/scripts/functional_tests.js index fc78e1b80bc681..4392299a78e72e 100644 --- a/x-pack/scripts/functional_tests.js +++ b/x-pack/scripts/functional_tests.js @@ -11,8 +11,9 @@ const alwaysImportedTests = [ require.resolve('../test/functional/config_security_trial.ts'), ]; const onlyNotInCoverageTests = [ - require.resolve('../test/api_integration/config_security_basic.js'), - require.resolve('../test/api_integration/config.js'), + require.resolve('../test/api_integration/config_security_basic.ts'), + require.resolve('../test/api_integration/config_security_trial.ts'), + require.resolve('../test/api_integration/config.ts'), require.resolve('../test/alerting_api_integration/basic/config.ts'), require.resolve('../test/alerting_api_integration/spaces_only/config.ts'), require.resolve('../test/alerting_api_integration/security_and_spaces/config.ts'), diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index e6b9c0bf5409f5..19aee29e9b36d6 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -40,7 +40,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) return async ({ readConfigFile }: FtrConfigProviderContext) => { const xPackApiIntegrationTestsConfig = await readConfigFile( - require.resolve('../../api_integration/config.js') + require.resolve('../../api_integration/config.ts') ); const servers = { ...xPackApiIntegrationTestsConfig.get('servers'), diff --git a/x-pack/test/api_integration/apis/security/license_downgrade.ts b/x-pack/test/api_integration/apis/security/license_downgrade.ts new file mode 100644 index 00000000000000..1811f99977b606 --- /dev/null +++ b/x-pack/test/api_integration/apis/security/license_downgrade.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect/expect.js'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('Privileges registration', function () { + this.tags(['skipCloud']); + + it('privileges are re-registered on license downgrade', async () => { + // Verify currently registered privileges for TRIAL license. + // If you're adding a privilege to the following, that's great! + // If you're removing a privilege, this breaks backwards compatibility + // Roles are associated with these privileges, and we shouldn't be removing them in a minor version. + const expectedTrialLicenseDiscoverPrivileges = [ + 'all', + 'read', + 'minimal_all', + 'minimal_read', + 'url_create', + ]; + const trialPrivileges = await supertest + .get('/api/security/privileges') + .set('kbn-xsrf', 'xxx') + .send() + .expect(200); + + expect(trialPrivileges.body.features.discover).to.eql(expectedTrialLicenseDiscoverPrivileges); + + // Revert license to basic. + await supertest + .post('/api/license/start_basic?acknowledge=true') + .set('kbn-xsrf', 'xxx') + .expect(200, { + basic_was_started: true, + acknowledged: true, + }); + + // Verify that privileges were re-registered. + const expectedBasicLicenseDiscoverPrivileges = ['all', 'read']; + const basicPrivileges = await supertest + .get('/api/security/privileges') + .set('kbn-xsrf', 'xxx') + .send() + .expect(200); + + expect(basicPrivileges.body.features.discover).to.eql(expectedBasicLicenseDiscoverPrivileges); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/security/security_trial.ts b/x-pack/test/api_integration/apis/security/security_trial.ts new file mode 100644 index 00000000000000..c646fecc1909f6 --- /dev/null +++ b/x-pack/test/api_integration/apis/security/security_trial.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('security (trial license)', function () { + this.tags('ciGroup6'); + + // THIS TEST NEEDS TO BE LAST. IT IS DESTRUCTIVE! IT REMOVES TRIAL LICENSE!!! + loadTestFile(require.resolve('./license_downgrade')); + }); +} diff --git a/x-pack/test/api_integration/config.js b/x-pack/test/api_integration/config.ts similarity index 94% rename from x-pack/test/api_integration/config.js rename to x-pack/test/api_integration/config.ts index 2e6e25aaebff00..71da903d33b29b 100644 --- a/x-pack/test/api_integration/config.js +++ b/x-pack/test/api_integration/config.ts @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import { services } from './services'; -export async function getApiIntegrationConfig({ readConfigFile }) { +export async function getApiIntegrationConfig({ readConfigFile }: FtrConfigProviderContext) { const xPackFunctionalTestsConfig = await readConfigFile( require.resolve('../functional/config.js') ); diff --git a/x-pack/test/api_integration/config_security_basic.js b/x-pack/test/api_integration/config_security_basic.ts similarity index 67% rename from x-pack/test/api_integration/config_security_basic.js rename to x-pack/test/api_integration/config_security_basic.ts index 713045e8c4d3cd..84899405056867 100644 --- a/x-pack/test/api_integration/config_security_basic.js +++ b/x-pack/test/api_integration/config_security_basic.ts @@ -4,11 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable import/no-default-export */ + +import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import { default as createTestConfig } from './config'; -export default async function ({ readConfigFile }) { - //security APIs should function the same under a basic or trial license - return createTestConfig({ readConfigFile }).then((config) => { +export default async function (context: FtrConfigProviderContext) { + // security APIs should function the same under a basic or trial license + return createTestConfig(context).then((config) => { config.esTestCluster.license = 'basic'; config.esTestCluster.serverArgs = [ 'xpack.license.self_generated.type=basic', diff --git a/x-pack/test/api_integration/config_security_trial.ts b/x-pack/test/api_integration/config_security_trial.ts new file mode 100644 index 00000000000000..4c1e2913b987c1 --- /dev/null +++ b/x-pack/test/api_integration/config_security_trial.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable import/no-default-export */ + +import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; +import { default as createTestConfig } from './config'; + +export default async function (context: FtrConfigProviderContext) { + return createTestConfig(context).then((config) => { + config.testFiles = [require.resolve('./apis/security/security_trial')]; + return config; + }); +} diff --git a/x-pack/test/apm_api_integration/common/config.ts b/x-pack/test/apm_api_integration/common/config.ts index 9e011a98bbfcd4..83dc597829a3cd 100644 --- a/x-pack/test/apm_api_integration/common/config.ts +++ b/x-pack/test/apm_api_integration/common/config.ts @@ -17,7 +17,7 @@ export function createTestConfig(settings: Settings) { return async ({ readConfigFile }: FtrConfigProviderContext) => { const xPackAPITestsConfig = await readConfigFile( - require.resolve('../../api_integration/config.js') + require.resolve('../../api_integration/config.ts') ); return { diff --git a/x-pack/test/case_api_integration/common/config.ts b/x-pack/test/case_api_integration/common/config.ts index 45b34b7d26940e..098d94f35d9ca2 100644 --- a/x-pack/test/case_api_integration/common/config.ts +++ b/x-pack/test/case_api_integration/common/config.ts @@ -39,7 +39,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) return async ({ readConfigFile }: FtrConfigProviderContext) => { const xPackApiIntegrationTestsConfig = await readConfigFile( - require.resolve('../../api_integration/config.js') + require.resolve('../../api_integration/config.ts') ); const servers = { diff --git a/x-pack/test/detection_engine_api_integration/common/config.ts b/x-pack/test/detection_engine_api_integration/common/config.ts index fa71a263b51a76..6a1add60b19f1e 100644 --- a/x-pack/test/detection_engine_api_integration/common/config.ts +++ b/x-pack/test/detection_engine_api_integration/common/config.ts @@ -42,7 +42,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) return async ({ readConfigFile }: FtrConfigProviderContext) => { const xPackApiIntegrationTestsConfig = await readConfigFile( - require.resolve('../../api_integration/config.js') + require.resolve('../../api_integration/config.ts') ); const servers = { ...xPackApiIntegrationTestsConfig.get('servers'), diff --git a/x-pack/test/encrypted_saved_objects_api_integration/config.ts b/x-pack/test/encrypted_saved_objects_api_integration/config.ts index f4b3091bec2343..fb643c2c5a901b 100644 --- a/x-pack/test/encrypted_saved_objects_api_integration/config.ts +++ b/x-pack/test/encrypted_saved_objects_api_integration/config.ts @@ -9,7 +9,7 @@ import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import { services } from './services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.js')); + const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); return { testFiles: [require.resolve('./tests')], diff --git a/x-pack/test/endpoint_api_integration_no_ingest/config.ts b/x-pack/test/endpoint_api_integration_no_ingest/config.ts index 00a65b9fbaec64..0cda1cfaf14338 100644 --- a/x-pack/test/endpoint_api_integration_no_ingest/config.ts +++ b/x-pack/test/endpoint_api_integration_no_ingest/config.ts @@ -7,7 +7,7 @@ import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.js')); + const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); return { ...xPackAPITestsConfig.getAll(), diff --git a/x-pack/test/epm_api_integration/config.ts b/x-pack/test/epm_api_integration/config.ts index b77d763313588e..6b08c7ec579559 100644 --- a/x-pack/test/epm_api_integration/config.ts +++ b/x-pack/test/epm_api_integration/config.ts @@ -7,7 +7,7 @@ import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.js')); + const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); return { testFiles: [require.resolve('./apis')], diff --git a/x-pack/test/kerberos_api_integration/config.ts b/x-pack/test/kerberos_api_integration/config.ts index 1e8e62e0484115..7b65d79e18e7dc 100644 --- a/x-pack/test/kerberos_api_integration/config.ts +++ b/x-pack/test/kerberos_api_integration/config.ts @@ -9,7 +9,7 @@ import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import { services } from './services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.js')); + const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); const kerberosKeytabPath = resolve( __dirname, diff --git a/x-pack/test/login_selector_api_integration/config.ts b/x-pack/test/login_selector_api_integration/config.ts index 33fa5df7cd1e08..ba7aadb121e825 100644 --- a/x-pack/test/login_selector_api_integration/config.ts +++ b/x-pack/test/login_selector_api_integration/config.ts @@ -12,7 +12,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { const kibanaAPITestsConfig = await readConfigFile( require.resolve('../../../test/api_integration/config.js') ); - const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.js')); + const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); const kibanaPort = xPackAPITestsConfig.get('servers.kibana.port'); const kerberosKeytabPath = resolve(__dirname, '../kerberos_api_integration/fixtures/krb5.keytab'); diff --git a/x-pack/test/observability_api_integration/common/config.ts b/x-pack/test/observability_api_integration/common/config.ts index 9e011a98bbfcd4..83dc597829a3cd 100644 --- a/x-pack/test/observability_api_integration/common/config.ts +++ b/x-pack/test/observability_api_integration/common/config.ts @@ -17,7 +17,7 @@ export function createTestConfig(settings: Settings) { return async ({ readConfigFile }: FtrConfigProviderContext) => { const xPackAPITestsConfig = await readConfigFile( - require.resolve('../../api_integration/config.js') + require.resolve('../../api_integration/config.ts') ); return { diff --git a/x-pack/test/oidc_api_integration/config.ts b/x-pack/test/oidc_api_integration/config.ts index a78d6c3febc2df..7a0d786e201301 100644 --- a/x-pack/test/oidc_api_integration/config.ts +++ b/x-pack/test/oidc_api_integration/config.ts @@ -9,7 +9,7 @@ import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import { services } from './services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.js')); + const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); const plugin = resolve(__dirname, './fixtures/oidc_provider'); const kibanaPort = xPackAPITestsConfig.get('servers.kibana.port'); const jwksPath = resolve(__dirname, './fixtures/jwks.json'); diff --git a/x-pack/test/pki_api_integration/config.ts b/x-pack/test/pki_api_integration/config.ts index e67ab0c6197f67..5ce3111530dd99 100644 --- a/x-pack/test/pki_api_integration/config.ts +++ b/x-pack/test/pki_api_integration/config.ts @@ -10,7 +10,7 @@ import { CA_CERT_PATH, KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; import { services } from './services'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.js')); + const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); const servers = { ...xPackAPITestsConfig.get('servers'), diff --git a/x-pack/test/saml_api_integration/config.ts b/x-pack/test/saml_api_integration/config.ts index d2aef5561dd11c..1bc85a803a7c21 100644 --- a/x-pack/test/saml_api_integration/config.ts +++ b/x-pack/test/saml_api_integration/config.ts @@ -11,7 +11,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { const kibanaAPITestsConfig = await readConfigFile( require.resolve('../../../test/api_integration/config.js') ); - const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.js')); + const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); const kibanaPort = xPackAPITestsConfig.get('servers.kibana.port'); const idpPath = resolve(__dirname, '../../test/saml_api_integration/fixtures/idp_metadata.xml'); diff --git a/x-pack/test/saved_object_api_integration/common/config.ts b/x-pack/test/saved_object_api_integration/common/config.ts index fc04a9774e023e..378f27082d9e00 100644 --- a/x-pack/test/saved_object_api_integration/common/config.ts +++ b/x-pack/test/saved_object_api_integration/common/config.ts @@ -24,7 +24,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) functional: await readConfigFile(require.resolve('../../../../test/functional/config.js')), }, xpack: { - api: await readConfigFile(require.resolve('../../api_integration/config.js')), + api: await readConfigFile(require.resolve('../../api_integration/config.ts')), }, }; diff --git a/x-pack/test/spaces_api_integration/common/config.ts b/x-pack/test/spaces_api_integration/common/config.ts index 27dc67b92b4e24..89a49c7d3d4fa5 100644 --- a/x-pack/test/spaces_api_integration/common/config.ts +++ b/x-pack/test/spaces_api_integration/common/config.ts @@ -25,7 +25,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) functional: await readConfigFile(require.resolve('../../../../test/functional/config.js')), }, xpack: { - api: await readConfigFile(require.resolve('../../api_integration/config.js')), + api: await readConfigFile(require.resolve('../../api_integration/config.ts')), }, }; diff --git a/x-pack/test/token_api_integration/config.js b/x-pack/test/token_api_integration/config.js index efb2813b489bb5..31ec2f1bc6bfb0 100644 --- a/x-pack/test/token_api_integration/config.js +++ b/x-pack/test/token_api_integration/config.js @@ -5,7 +5,7 @@ */ export default async function ({ readConfigFile }) { - const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.js')); + const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); return { testFiles: [require.resolve('./auth')], diff --git a/x-pack/tsconfig.json b/x-pack/tsconfig.json index 9bba90291b8847..e0bf3b8f169462 100644 --- a/x-pack/tsconfig.json +++ b/x-pack/tsconfig.json @@ -27,9 +27,6 @@ "plugins/xpack_main/*": [ "x-pack/legacy/plugins/xpack_main/public/*" ], - "plugins/security/*": [ - "x-pack/legacy/plugins/security/public/*" - ], "plugins/spaces/*": [ "x-pack/legacy/plugins/spaces/public/*" ], diff --git a/x-pack/typings/hapi.d.ts b/x-pack/typings/hapi.d.ts index 6af723101fc223..253b639a52ff21 100644 --- a/x-pack/typings/hapi.d.ts +++ b/x-pack/typings/hapi.d.ts @@ -7,7 +7,6 @@ import 'hapi'; import { XPackMainPlugin } from '../legacy/plugins/xpack_main/server/xpack_main'; -import { SecurityPlugin } from '../legacy/plugins/security'; import { ActionsPlugin, ActionsClient } from '../plugins/actions/server'; import { AlertingPlugin, AlertsClient } from '../plugins/alerts/server'; import { TaskManager } from '../plugins/task_manager/server'; @@ -19,7 +18,6 @@ declare module 'hapi' { } interface PluginProperties { xpack_main: XPackMainPlugin; - security?: SecurityPlugin; actions?: ActionsPlugin; alerts?: AlertingPlugin; task_manager?: TaskManager;