Register privileges in Kibana Platform Security plugin and remove legacy getUser API. (#65472)

This commit is contained in:
Aleh Zasypkin 2020-06-05 08:11:58 +02:00 committed by GitHub
parent 6ef2a2c07e
commit c6e2fed1c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 721 additions and 413 deletions

View file

@ -4,7 +4,6 @@ files:
- 'src/legacy/core_plugins/timelion/**/*.s+(a|c)ss' - 'src/legacy/core_plugins/timelion/**/*.s+(a|c)ss'
- 'src/plugins/vis_type_vislib/**/*.s+(a|c)ss' - 'src/plugins/vis_type_vislib/**/*.s+(a|c)ss'
- 'src/plugins/vis_type_xy/**/*.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/canvas/**/*.s+(a|c)ss'
- 'x-pack/plugins/triggers_actions_ui/**/*.s+(a|c)ss' - 'x-pack/plugins/triggers_actions_ui/**/*.s+(a|c)ss'
- 'x-pack/plugins/lens/**/*.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/legacy/plugins/maps/**/*.s+(a|c)ss'
- 'x-pack/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/spaces/**/*.s+(a|c)ss'
- 'x-pack/plugins/security/**/*.s+(a|c)ss'
ignore: ignore:
- 'x-pack/plugins/canvas/shareable_runtime/**/*.s+(a|c)ss' - 'x-pack/plugins/canvas/shareable_runtime/**/*.s+(a|c)ss'
rules: rules:

View file

@ -39,7 +39,7 @@
"xpack.reporting": ["plugins/reporting"], "xpack.reporting": ["plugins/reporting"],
"xpack.rollupJobs": ["legacy/plugins/rollup", "plugins/rollup"], "xpack.rollupJobs": ["legacy/plugins/rollup", "plugins/rollup"],
"xpack.searchProfiler": "plugins/searchprofiler", "xpack.searchProfiler": "plugins/searchprofiler",
"xpack.security": ["legacy/plugins/security", "plugins/security"], "xpack.security": "plugins/security",
"xpack.server": "legacy/server", "xpack.server": "legacy/server",
"xpack.securitySolution": "plugins/security_solution", "xpack.securitySolution": "plugins/security_solution",
"xpack.snapshotRestore": "plugins/snapshot_restore", "xpack.snapshotRestore": "plugins/snapshot_restore",

View file

@ -25,8 +25,8 @@ Examples:
- Run the jest test case whose description matches 'filtering should skip values of null': - 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` `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: - 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_tests_server --config x-pack/test/api_integration/config.ts`
`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_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(`. 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). 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: The script runs all sets of tests sequentially like so:
* builds Elasticsearch and X-Pack * builds Elasticsearch and X-Pack

View file

@ -15,7 +15,7 @@ In one shell, from **~/kibana/x-pack**:
`node scripts/functional_tests-server.js` `node scripts/functional_tests-server.js`
In another shell, from **~kibana/x-pack**: 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 ### Manual e2e testing

View file

@ -8,6 +8,7 @@
import { Lifecycle, ResponseToolkit } from 'hapi'; import { Lifecycle, ResponseToolkit } from 'hapi';
import * as t from 'io-ts'; import * as t from 'io-ts';
import { SecurityPluginSetup } from '../../../../../../../plugins/security/server';
import { LicenseType } from '../../../../common/constants/security'; import { LicenseType } from '../../../../common/constants/security';
export const internalAuthData = Symbol('internalAuthData'); export const internalAuthData = Symbol('internalAuthData');
@ -39,6 +40,11 @@ export interface BackendFrameworkAdapter {
} }
export interface KibanaLegacyServer { export interface KibanaLegacyServer {
newPlatform: {
setup: {
plugins: { security: SecurityPluginSetup };
};
};
plugins: { plugins: {
xpack_main: { xpack_main: {
status: { status: {
@ -53,9 +59,6 @@ export interface KibanaLegacyServer {
}; };
}; };
}; };
security: {
getUser: (request: KibanaServerRequest) => any;
};
elasticsearch: { elasticsearch: {
status: { status: {
on: (status: 'green' | 'yellow' | 'red', callback: () => void) => void; on: (status: 'green' | 'yellow' | 'red', callback: () => void) => void;

View file

@ -8,6 +8,7 @@ import { ResponseToolkit } from 'hapi';
import { PathReporter } from 'io-ts/lib/PathReporter'; import { PathReporter } from 'io-ts/lib/PathReporter';
import { get } from 'lodash'; import { get } from 'lodash';
import { isLeft } from 'fp-ts/lib/Either'; import { isLeft } from 'fp-ts/lib/Either';
import { KibanaRequest, LegacyRequest } from '../../../../../../../../src/core/server';
// @ts-ignore // @ts-ignore
import { mirrorPluginStatus } from '../../../../../../server/lib/mirror_plugin_status'; import { mirrorPluginStatus } from '../../../../../../server/lib/mirror_plugin_status';
import { import {
@ -128,13 +129,10 @@ export class KibanaBackendFrameworkAdapter implements BackendFrameworkAdapter {
} }
private async getUser(request: KibanaServerRequest): Promise<KibanaUser | null> { private async getUser(request: KibanaServerRequest): Promise<KibanaUser | null> {
let user; const user = this.server.newPlatform.setup.plugins.security?.authc.getCurrentUser(
try { KibanaRequest.from((request as unknown) as LegacyRequest)
user = await this.server.plugins.security.getUser(request); );
} catch (e) { if (!user) {
return null;
}
if (user === null) {
return null; return null;
} }
const assertKibanaUser = RuntimeKibanaUser.decode(user); const assertKibanaUser = RuntimeKibanaUser.decode(user);

View file

@ -6,64 +6,17 @@
import { Root } from 'joi'; import { Root } from 'joi';
import { resolve } from 'path'; 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<AuthenticatedUser>;
}
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<string, any>) => export const security = (kibana: Record<string, any>) =>
new kibana.Plugin({ new kibana.Plugin({
id: 'security', id: 'security',
publicDir: resolve(__dirname, 'public'), publicDir: resolve(__dirname, 'public'),
require: ['kibana', 'xpack_main'], require: ['kibana'],
configPrefix: 'xpack.security', configPrefix: 'xpack.security',
uiExports: { uiExports: { hacks: ['plugins/security/hacks/legacy'] },
hacks: ['plugins/security/hacks/legacy'], config: (Joi: Root) =>
injectDefaultVars: (server: Server) => { Joi.object({ enabled: Joi.boolean().default(true) })
return { enableSpaceAwarePrivileges: server.config().get('xpack.spaces.enabled') };
},
},
config(Joi: Root) {
return Joi.object({
enabled: Joi.boolean().default(true),
})
.unknown() .unknown()
.default(); .default(),
}, init() {},
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)),
});
},
}); });

View file

@ -83,13 +83,13 @@ For debugging access Elasticsearch on http://localhost:9220` (elastic/changeme)
**Start server** **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** **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`. APM tests are located in `x-pack/test/api_integration/apis/apm`.

View file

@ -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 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 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 #### EPM

View file

@ -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. - You may want to comment out all imports except for Lens in the config file.
- API Functional tests: - API Functional tests:
- Run `node scripts/functional_tests_server` - 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`

View file

@ -5,7 +5,12 @@
*/ */
import { i18n } from '@kbn/i18n'; 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'; import { AuthenticationServiceSetup } from '../authentication';
interface CreateDeps { interface CreateDeps {
@ -23,8 +28,7 @@ export const accountManagementApp = Object.freeze({
application.register({ application.register({
id: this.id, id: this.id,
title, title,
// TODO: switch to proper enum once https://github.com/elastic/kibana/issues/58327 is resolved. navLinkStatus: AppNavLinkStatus.hidden,
navLinkStatus: 3,
appRoute: '/security/account', appRoute: '/security/account',
async mount({ element }: AppMountParameters) { async mount({ element }: AppMountParameters) {
const [ const [

View file

@ -23,9 +23,10 @@
} }
&:focus { &:focus {
@include euiFocusRing;
border-color: transparent; border-color: transparent;
border-radius: $euiBorderRadius; border-radius: $euiBorderRadius;
@include euiFocusRing;
.secLoginCard__title { .secLoginCard__title {
text-decoration: underline; text-decoration: underline;

View file

@ -11,13 +11,15 @@ import { Feature } from '../../../../../features/public';
import { KibanaPrivileges } from '../model'; import { KibanaPrivileges } from '../model';
import { SecurityLicenseFeatures } from '../../..'; import { SecurityLicenseFeatures } from '../../..';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { featuresPluginMock } from '../../../../../features/server/mocks';
export const createRawKibanaPrivileges = ( export const createRawKibanaPrivileges = (
features: Feature[], features: Feature[],
{ allowSubFeaturePrivileges = true } = {} { allowSubFeaturePrivileges = true } = {}
) => { ) => {
const featuresService = { const featuresService = featuresPluginMock.createSetup();
getFeatures: () => features, featuresService.getFeatures.mockReturnValue(features);
};
const licensingService = { const licensingService = {
getFeatures: () => ({ allowSubFeaturePrivileges } as SecurityLicenseFeatures), getFeatures: () => ({ allowSubFeaturePrivileges } as SecurityLicenseFeatures),

View file

@ -163,7 +163,12 @@ function getProps({
const { http, docLinks, notifications } = coreMock.createStart(); const { http, docLinks, notifications } = coreMock.createStart();
http.get.mockImplementation(async (path: any) => { http.get.mockImplementation(async (path: any) => {
if (path === '/api/spaces/space') { 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, notifications,
docLinks: new DocumentationLinksService(docLinks), docLinks: new DocumentationLinksService(docLinks),
fatalErrors, fatalErrors,
spacesEnabled,
uiCapabilities: buildUICapabilities(canManageSpaces), uiCapabilities: buildUICapabilities(canManageSpaces),
history: (scopedHistoryMock.create() as unknown) as ScopedHistory, history: (scopedHistoryMock.create() as unknown) as ScopedHistory,
}; };

View file

@ -80,7 +80,6 @@ interface Props {
docLinks: DocumentationLinksService; docLinks: DocumentationLinksService;
http: HttpStart; http: HttpStart;
license: SecurityLicense; license: SecurityLicense;
spacesEnabled: boolean;
uiCapabilities: Capabilities; uiCapabilities: Capabilities;
notifications: NotificationsStart; notifications: NotificationsStart;
fatalErrors: FatalErrorsSetup; fatalErrors: FatalErrorsSetup;
@ -225,14 +224,21 @@ function useRole(
return [role, setRole] as [Role | null, typeof setRole]; return [role, setRole] as [Role | null, typeof setRole];
} }
function useSpaces(http: HttpStart, fatalErrors: FatalErrorsSetup, spacesEnabled: boolean) { function useSpaces(http: HttpStart, fatalErrors: FatalErrorsSetup) {
const [spaces, setSpaces] = useState<Space[] | null>(null); const [spaces, setSpaces] = useState<{ enabled: boolean; list: Space[] } | null>(null);
useEffect(() => { useEffect(() => {
(spacesEnabled ? http.get('/api/spaces/space') : Promise.resolve([])).then( http.get('/api/spaces/space').then(
(fetchedSpaces) => setSpaces(fetchedSpaces), (fetchedSpaces) => setSpaces({ enabled: true, list: fetchedSpaces }),
(err) => fatalErrors.add(err) (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; return spaces;
} }
@ -278,7 +284,6 @@ export const EditRolePage: FunctionComponent<Props> = ({
roleName, roleName,
action, action,
fatalErrors, fatalErrors,
spacesEnabled,
license, license,
docLinks, docLinks,
uiCapabilities, uiCapabilities,
@ -295,7 +300,7 @@ export const EditRolePage: FunctionComponent<Props> = ({
const runAsUsers = useRunAsUsers(userAPIClient, fatalErrors); const runAsUsers = useRunAsUsers(userAPIClient, fatalErrors);
const indexPatternsTitles = useIndexPatternsTitles(indexPatterns, fatalErrors, notifications); const indexPatternsTitles = useIndexPatternsTitles(indexPatterns, fatalErrors, notifications);
const privileges = usePrivileges(privilegesAPIClient, fatalErrors); const privileges = usePrivileges(privilegesAPIClient, fatalErrors);
const spaces = useSpaces(http, fatalErrors, spacesEnabled); const spaces = useSpaces(http, fatalErrors);
const features = useFeatures(getFeatures, fatalErrors); const features = useFeatures(getFeatures, fatalErrors);
const [role, setRole] = useRole( const [role, setRole] = useRole(
rolesAPIClient, rolesAPIClient,
@ -434,8 +439,8 @@ export const EditRolePage: FunctionComponent<Props> = ({
<EuiSpacer /> <EuiSpacer />
<KibanaPrivilegesRegion <KibanaPrivilegesRegion
kibanaPrivileges={new KibanaPrivileges(kibanaPrivileges, features)} kibanaPrivileges={new KibanaPrivileges(kibanaPrivileges, features)}
spaces={spaces} spaces={spaces.list}
spacesEnabled={spacesEnabled} spacesEnabled={spaces.enabled}
uiCapabilities={uiCapabilities} uiCapabilities={uiCapabilities}
canCustomizeSubFeaturePrivileges={license.getFeatures().allowSubFeaturePrivileges} canCustomizeSubFeaturePrivileges={license.getFeatures().allowSubFeaturePrivileges}
editable={!isRoleReadOnly} editable={!isRoleReadOnly}
@ -519,7 +524,7 @@ export const EditRolePage: FunctionComponent<Props> = ({
setFormError(null); setFormError(null);
try { try {
await rolesAPIClient.saveRole({ role, spacesEnabled }); await rolesAPIClient.saveRole({ role, spacesEnabled: spaces.enabled });
} catch (error) { } catch (error) {
notifications.toasts.addDanger(get(error, 'data.message')); notifications.toasts.addDanger(get(error, 'data.message'));
return; return;
@ -554,7 +559,7 @@ export const EditRolePage: FunctionComponent<Props> = ({
backToRoleList(); backToRoleList();
}; };
const description = spacesEnabled ? ( const description = spaces.enabled ? (
<FormattedMessage <FormattedMessage
id="xpack.security.management.editRole.setPrivilegesToKibanaSpacesDescription" id="xpack.security.management.editRole.setPrivilegesToKibanaSpacesDescription"
defaultMessage="Set privileges on your Elasticsearch data and control access to your Kibana spaces." defaultMessage="Set privileges on your Elasticsearch data and control access to your Kibana spaces."

View file

@ -36,10 +36,7 @@ export const rolesManagementApp = Object.freeze({
]; ];
const [ const [
[ [{ application, docLinks, http, i18n: i18nStart, notifications }, { data, features }],
{ application, docLinks, http, i18n: i18nStart, injectedMetadata, notifications },
{ data, features },
],
{ RolesGridPage }, { RolesGridPage },
{ EditRolePage }, { EditRolePage },
{ RolesAPIClient }, { RolesAPIClient },
@ -86,9 +83,6 @@ export const rolesManagementApp = Object.freeze({
<EditRolePage <EditRolePage
action={action} action={action}
roleName={roleName} roleName={roleName}
spacesEnabled={
injectedMetadata.getInjectedVar('enableSpaceAwarePrivileges') as boolean
}
rolesAPIClient={rolesAPIClient} rolesAPIClient={rolesAPIClient}
userAPIClient={new UserAPIClient(http)} userAPIClient={new UserAPIClient(http)}
indicesAPIClient={new IndicesAPIClient(http)} indicesAPIClient={new IndicesAPIClient(http)}

View file

@ -4,12 +4,12 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { CoreSetup, Logger } from '../../../../../src/core/server'; import { HttpServiceSetup, Logger } from '../../../../../src/core/server';
import { Authorization } from '.'; import { AuthorizationServiceSetup } from '.';
export function initAPIAuthorization( export function initAPIAuthorization(
http: CoreSetup['http'], http: HttpServiceSetup,
{ actions, checkPrivilegesDynamicallyWithRequest, mode }: Authorization, { actions, checkPrivilegesDynamicallyWithRequest, mode }: AuthorizationServiceSetup,
logger: Logger logger: Logger
) { ) {
http.registerOnPostAuth(async (request, response, toolkit) => { http.registerOnPostAuth(async (request, response, toolkit) => {

View file

@ -4,13 +4,13 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { CoreSetup, Logger } from '../../../../../src/core/server'; import { HttpServiceSetup, Logger } from '../../../../../src/core/server';
import { FeaturesService } from '../plugin'; import { PluginSetupContract as FeaturesPluginSetup } from '../../../features/server';
import { Authorization } from '.'; import { AuthorizationServiceSetup } from '.';
class ProtectedApplications { class ProtectedApplications {
private applications: Set<string> | null = null; private applications: Set<string> | null = null;
constructor(private readonly featuresService: FeaturesService) {} constructor(private readonly featuresService: FeaturesPluginSetup) {}
public shouldProtect(appId: string) { public shouldProtect(appId: string) {
// Currently, once we get the list of features we essentially "lock" additional // Currently, once we get the list of features we essentially "lock" additional
@ -30,14 +30,14 @@ class ProtectedApplications {
} }
export function initAppAuthorization( export function initAppAuthorization(
http: CoreSetup['http'], http: HttpServiceSetup,
{ {
actions, actions,
checkPrivilegesDynamicallyWithRequest, checkPrivilegesDynamicallyWithRequest,
mode, mode,
}: Pick<Authorization, 'actions' | 'checkPrivilegesDynamicallyWithRequest' | 'mode'>, }: Pick<AuthorizationServiceSetup, 'actions' | 'checkPrivilegesDynamicallyWithRequest' | 'mode'>,
logger: Logger, logger: Logger,
featuresService: FeaturesService featuresService: FeaturesPluginSetup
) { ) {
const protectedApplications = new ProtectedApplications(featuresService); const protectedApplications = new ProtectedApplications(featuresService);

View file

@ -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<CoreStatus>;
let licenseSubject: BehaviorSubject<SecurityLicenseFeatures>;
let mockLicense: jest.Mocked<SecurityLicense>;
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<CoreStatus>({
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<CoreStatus>({
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();
});

View file

@ -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)
);
}
});
}
}

View file

@ -10,13 +10,13 @@ import { KibanaRequest, Logger } from '../../../../../src/core/server';
import { Feature } from '../../../features/server'; import { Feature } from '../../../features/server';
import { CheckPrivilegesResponse } from './check_privileges'; import { CheckPrivilegesResponse } from './check_privileges';
import { Authorization } from './index'; import { AuthorizationServiceSetup } from '.';
export function disableUICapabilitiesFactory( export function disableUICapabilitiesFactory(
request: KibanaRequest, request: KibanaRequest,
features: Feature[], features: Feature[],
logger: Logger, logger: Logger,
authz: Authorization authz: AuthorizationServiceSetup
) { ) {
const featureNavLinkIds = features const featureNavLinkIds = features
.map((feature) => feature.navLinkId) .map((feature) => feature.navLinkId)

View file

@ -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);
});

View file

@ -4,134 +4,7 @@
* you may not use this file except in compliance with the Elastic License. * 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 { Actions } from './actions';
export { AuthorizationService, AuthorizationServiceSetup } from './authorization_service';
export { CheckSavedObjectsPrivileges } from './check_saved_objects_privileges'; export { CheckSavedObjectsPrivileges } from './check_saved_objects_privileges';
export { featurePrivilegeIterator } from './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<UICapabilities>;
registerPrivilegesWithCluster: () => Promise<void>;
}
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;
}

View file

@ -8,6 +8,8 @@ import { Feature } from '../../../../features/server';
import { Actions } from '../actions'; import { Actions } from '../actions';
import { privilegesFactory } from './privileges'; import { privilegesFactory } from './privileges';
import { featuresPluginMock } from '../../../../features/server/mocks';
const actions = new Actions('1.0.0-zeta1'); const actions = new Actions('1.0.0-zeta1');
describe('features', () => { 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 = { const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
}; };

View file

@ -6,11 +6,10 @@
import { uniq } from 'lodash'; import { uniq } from 'lodash';
import { SecurityLicense } from '../../../common/licensing'; import { SecurityLicense } from '../../../common/licensing';
import { Feature } from '../../../../features/server'; import { Feature, PluginSetupContract as FeaturesPluginSetup } from '../../../../features/server';
import { RawKibanaPrivileges } from '../../../common/model'; import { RawKibanaPrivileges } from '../../../common/model';
import { Actions } from '../actions'; import { Actions } from '../actions';
import { featurePrivilegeBuilderFactory } from './feature_privilege_builder'; import { featurePrivilegeBuilderFactory } from './feature_privilege_builder';
import { FeaturesService } from '../../plugin';
import { import {
featurePrivilegeIterator, featurePrivilegeIterator,
subFeaturePrivilegeIterator, subFeaturePrivilegeIterator,
@ -22,7 +21,7 @@ export interface PrivilegesService {
export function privilegesFactory( export function privilegesFactory(
actions: Actions, actions: Actions,
featuresService: FeaturesService, featuresService: FeaturesPluginSetup,
licenseService: Pick<SecurityLicense, 'getFeatures'> licenseService: Pick<SecurityLicense, 'getFeatures'>
) { ) {
const featurePrivilegeBuilder = featurePrivilegeBuilderFactory(actions); const featurePrivilegeBuilder = featurePrivilegeBuilderFactory(actions);

View file

@ -49,7 +49,7 @@ const registerPrivilegesWithClusterTest = (
}); });
for (const deletedPrivilege of deletedPrivileges) { for (const deletedPrivilege of deletedPrivileges) {
expect(mockLogger.debug).toHaveBeenCalledWith( expect(mockLogger.debug).toHaveBeenCalledWith(
`Deleting Kibana Privilege ${deletedPrivilege} from Elasticearch for ${application}` `Deleting Kibana Privilege ${deletedPrivilege} from Elasticsearch for ${application}`
); );
expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith(
'shield.deletePrivilege', 'shield.deletePrivilege',
@ -82,7 +82,7 @@ const registerPrivilegesWithClusterTest = (
`Registering Kibana Privileges with Elasticsearch for ${application}` `Registering Kibana Privileges with Elasticsearch for ${application}`
); );
expect(mockLogger.debug).toHaveBeenCalledWith( expect(mockLogger.debug).toHaveBeenCalledWith(
`Kibana Privileges already registered with Elasticearch for ${application}` `Kibana Privileges already registered with Elasticsearch for ${application}`
); );
}; };
}; };

View file

@ -61,14 +61,14 @@ export async function registerPrivilegesWithCluster(
privilege: application, privilege: application,
}); });
if (arePrivilegesEqual(existingPrivileges, expectedPrivileges)) { 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; return;
} }
const privilegesToDelete = getPrivilegesToDelete(existingPrivileges, expectedPrivileges); const privilegesToDelete = getPrivilegesToDelete(existingPrivileges, expectedPrivileges);
for (const privilegeToDelete of privilegesToDelete) { for (const privilegeToDelete of privilegesToDelete) {
logger.debug( logger.debug(
`Deleting Kibana Privilege ${privilegeToDelete} from Elasticearch for ${application}` `Deleting Kibana Privilege ${privilegeToDelete} from Elasticsearch for ${application}`
); );
try { try {
await clusterClient.callAsInternalUser('shield.deletePrivilege', { await clusterClient.callAsInternalUser('shield.deletePrivilege', {

View file

@ -28,3 +28,8 @@ export const mockAuthorizationModeFactory = jest.fn();
jest.mock('./mode', () => ({ jest.mock('./mode', () => ({
authorizationModeFactory: mockAuthorizationModeFactory, authorizationModeFactory: mockAuthorizationModeFactory,
})); }));
export const mockRegisterPrivilegesWithCluster = jest.fn();
jest.mock('./register_privileges_with_cluster', () => ({
registerPrivilegesWithCluster: mockRegisterPrivilegesWithCluster,
}));

View file

@ -4,8 +4,6 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { SecurityPluginSetup } from './plugin';
import { authenticationMock } from './authentication/index.mock'; import { authenticationMock } from './authentication/index.mock';
import { authorizationMock } from './authorization/index.mock'; import { authorizationMock } from './authorization/index.mock';
import { licenseMock } from '../common/licensing/index.mock'; import { licenseMock } from '../common/licensing/index.mock';
@ -23,7 +21,6 @@ function createSetupMock() {
}, },
registerSpacesService: jest.fn(), registerSpacesService: jest.fn(),
license: licenseMock.create(), license: licenseMock.create(),
__legacyCompat: {} as SecurityPluginSetup['__legacyCompat'],
}; };
} }

View file

@ -50,9 +50,6 @@ describe('Security Plugin', () => {
it('exposes proper contract', async () => { it('exposes proper contract', async () => {
await expect(plugin.setup(mockCoreSetup, mockDependencies)).resolves.toMatchInlineSnapshot(` await expect(plugin.setup(mockCoreSetup, mockDependencies)).resolves.toMatchInlineSnapshot(`
Object { Object {
"__legacyCompat": Object {
"registerPrivilegesWithCluster": [Function],
},
"audit": Object { "audit": Object {
"getLogger": [Function], "getLogger": [Function],
}, },

View file

@ -8,22 +8,22 @@ import { combineLatest } from 'rxjs';
import { first, map } from 'rxjs/operators'; import { first, map } from 'rxjs/operators';
import { TypeOf } from '@kbn/config-schema'; import { TypeOf } from '@kbn/config-schema';
import { import {
deepFreeze,
ICustomClusterClient, ICustomClusterClient,
CoreSetup, CoreSetup,
CoreStart,
Logger, Logger,
PluginInitializerContext, PluginInitializerContext,
CoreStart,
} from '../../../../src/core/server'; } from '../../../../src/core/server';
import { deepFreeze } from '../../../../src/core/server';
import { SpacesPluginSetup } from '../../spaces/server'; import { SpacesPluginSetup } from '../../spaces/server';
import { import {
PluginSetupContract as FeaturesSetupContract, PluginSetupContract as FeaturesPluginSetup,
PluginStartContract as FeaturesStartContract, PluginStartContract as FeaturesPluginStart,
} from '../../features/server'; } from '../../features/server';
import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/server'; import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/server';
import { Authentication, setupAuthentication } from './authentication'; import { Authentication, setupAuthentication } from './authentication';
import { Authorization, setupAuthorization } from './authorization'; import { AuthorizationService, AuthorizationServiceSetup } from './authorization';
import { ConfigSchema, createConfig } from './config'; import { ConfigSchema, createConfig } from './config';
import { defineRoutes } from './routes'; import { defineRoutes } from './routes';
import { SecurityLicenseService, SecurityLicense } from '../common/licensing'; import { SecurityLicenseService, SecurityLicense } from '../common/licensing';
@ -37,8 +37,6 @@ export type SpacesService = Pick<
'getSpaceId' | 'namespaceToSpaceId' 'getSpaceId' | 'namespaceToSpaceId'
>; >;
export type FeaturesService = Pick<FeaturesSetupContract, 'getFeatures'>;
/** /**
* Describes public Security plugin contract returned at the `setup` stage. * Describes public Security plugin contract returned at the `setup` stage.
*/ */
@ -53,7 +51,7 @@ export interface SecurityPluginSetup {
| 'grantAPIKeyAsInternalUser' | 'grantAPIKeyAsInternalUser'
| 'invalidateAPIKeyAsInternalUser' | 'invalidateAPIKeyAsInternalUser'
>; >;
authz: Pick<Authorization, 'actions' | 'checkPrivilegesWithRequest' | 'mode'>; authz: Pick<AuthorizationServiceSetup, 'actions' | 'checkPrivilegesWithRequest' | 'mode'>;
license: SecurityLicense; license: SecurityLicense;
audit: Pick<AuditServiceSetup, 'getLogger'>; audit: Pick<AuditServiceSetup, 'getLogger'>;
@ -66,19 +64,15 @@ export interface SecurityPluginSetup {
* @param service Spaces service exposed by the Spaces plugin. * @param service Spaces service exposed by the Spaces plugin.
*/ */
registerSpacesService: (service: SpacesService) => void; registerSpacesService: (service: SpacesService) => void;
__legacyCompat: {
registerPrivilegesWithCluster: () => void;
};
} }
export interface PluginSetupDependencies { export interface PluginSetupDependencies {
features: FeaturesService; features: FeaturesPluginSetup;
licensing: LicensingPluginSetup; licensing: LicensingPluginSetup;
} }
export interface PluginStartDependencies { export interface PluginStartDependencies {
features: FeaturesStartContract; features: FeaturesPluginStart;
licensing: LicensingPluginStart; licensing: LicensingPluginStart;
} }
@ -101,6 +95,7 @@ export class Plugin {
}; };
private readonly auditService = new AuditService(this.initializerContext.logger.get('audit')); private readonly auditService = new AuditService(this.initializerContext.logger.get('audit'));
private readonly authorizationService = new AuthorizationService();
private readonly getSpacesService = () => { private readonly getSpacesService = () => {
// Changing property value from Symbol to undefined denotes the fact that property was accessed. // 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, loggers: this.initializerContext.logger,
}); });
const authz = await setupAuthorization({ const authz = this.authorizationService.setup({
http: core.http, http: core.http,
capabilities: core.capabilities,
status: core.status,
clusterClient: this.clusterClient, clusterClient: this.clusterClient,
license, license,
loggers: this.initializerContext.logger, loggers: this.initializerContext.logger,
kibanaIndexName: legacyConfig.kibana.index, kibanaIndexName: legacyConfig.kibana.index,
packageVersion: this.initializerContext.env.packageInfo.version, packageVersion: this.initializerContext.env.packageInfo.version,
getSpacesService: this.getSpacesService, getSpacesService: this.getSpacesService,
featuresService: features, features,
}); });
setupSavedObjects({ setupSavedObjects({
@ -174,8 +171,6 @@ export class Plugin {
getSpacesService: this.getSpacesService, getSpacesService: this.getSpacesService,
}); });
core.capabilities.registerSwitcher(authz.disableUnauthorizedCapabilities);
defineRoutes({ defineRoutes({
router: core.http.createRouter(), router: core.http.createRouter(),
basePath: core.http.basePath, basePath: core.http.basePath,
@ -223,18 +218,15 @@ export class Plugin {
this.spacesService = service; 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.logger.debug('Starting plugin');
this.featureUsageServiceStart = this.featureUsageService.start({ this.featureUsageServiceStart = this.featureUsageService.start({
featureUsage: licensing.featureUsage, featureUsage: licensing.featureUsage,
}); });
this.authorizationService.start({ features, clusterClient: this.clusterClient! });
} }
public stop() { public stop() {
@ -254,6 +246,7 @@ export class Plugin {
this.featureUsageServiceStart = undefined; this.featureUsageServiceStart = undefined;
} }
this.auditService.stop(); this.auditService.stop();
this.authorizationService.stop();
} }
private wasSpacesServiceAccessed() { private wasSpacesServiceAccessed() {

View file

@ -14,7 +14,7 @@ import {
} from '../../../../../src/core/server'; } from '../../../../../src/core/server';
import { SecurityLicense } from '../../common/licensing'; import { SecurityLicense } from '../../common/licensing';
import { Authentication } from '../authentication'; import { Authentication } from '../authentication';
import { Authorization } from '../authorization'; import { AuthorizationServiceSetup } from '../authorization';
import { ConfigType } from '../config'; import { ConfigType } from '../config';
import { defineAuthenticationRoutes } from './authentication'; import { defineAuthenticationRoutes } from './authentication';
@ -37,7 +37,7 @@ export interface RouteDefinitionParams {
clusterClient: IClusterClient; clusterClient: IClusterClient;
config: ConfigType; config: ConfigType;
authc: Authentication; authc: Authentication;
authz: Authorization; authz: AuthorizationServiceSetup;
license: SecurityLicense; license: SecurityLicense;
getFeatures: () => Promise<Feature[]>; getFeatures: () => Promise<Feature[]>;
getFeatureUsageService: () => SecurityFeatureUsageServiceStart; getFeatureUsageService: () => SecurityFeatureUsageServiceStart;

View file

@ -11,13 +11,16 @@ import {
SavedObjectsClient, SavedObjectsClient,
} from '../../../../../src/core/server'; } from '../../../../../src/core/server';
import { SecureSavedObjectsClientWrapper } from './secure_saved_objects_client_wrapper'; import { SecureSavedObjectsClientWrapper } from './secure_saved_objects_client_wrapper';
import { Authorization } from '../authorization'; import { AuthorizationServiceSetup } from '../authorization';
import { SecurityAuditLogger } from '../audit'; import { SecurityAuditLogger } from '../audit';
import { SpacesService } from '../plugin'; import { SpacesService } from '../plugin';
interface SetupSavedObjectsParams { interface SetupSavedObjectsParams {
auditLogger: SecurityAuditLogger; auditLogger: SecurityAuditLogger;
authz: Pick<Authorization, 'mode' | 'actions' | 'checkSavedObjectsPrivilegesWithRequest'>; authz: Pick<
AuthorizationServiceSetup,
'mode' | 'actions' | 'checkSavedObjectsPrivilegesWithRequest'
>;
savedObjects: CoreSetup['savedObjects']; savedObjects: CoreSetup['savedObjects'];
getSpacesService(): SpacesService | undefined; getSpacesService(): SpacesService | undefined;
} }

View file

@ -55,7 +55,7 @@ In another shell, from **~kibana/x-pack**:
#### API tests #### API tests
If instead you need to run API tests, start up the test server and then in another shell, from **~kibana/x-pack**: 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` You can update snapshots by prefixing the runner command with `env UPDATE_UPTIME_FIXTURES=1`

View file

@ -11,8 +11,9 @@ const alwaysImportedTests = [
require.resolve('../test/functional/config_security_trial.ts'), require.resolve('../test/functional/config_security_trial.ts'),
]; ];
const onlyNotInCoverageTests = [ const onlyNotInCoverageTests = [
require.resolve('../test/api_integration/config_security_basic.js'), require.resolve('../test/api_integration/config_security_basic.ts'),
require.resolve('../test/api_integration/config.js'), 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/basic/config.ts'),
require.resolve('../test/alerting_api_integration/spaces_only/config.ts'), require.resolve('../test/alerting_api_integration/spaces_only/config.ts'),
require.resolve('../test/alerting_api_integration/security_and_spaces/config.ts'), require.resolve('../test/alerting_api_integration/security_and_spaces/config.ts'),

View file

@ -40,7 +40,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
return async ({ readConfigFile }: FtrConfigProviderContext) => { return async ({ readConfigFile }: FtrConfigProviderContext) => {
const xPackApiIntegrationTestsConfig = await readConfigFile( const xPackApiIntegrationTestsConfig = await readConfigFile(
require.resolve('../../api_integration/config.js') require.resolve('../../api_integration/config.ts')
); );
const servers = { const servers = {
...xPackApiIntegrationTestsConfig.get('servers'), ...xPackApiIntegrationTestsConfig.get('servers'),

View file

@ -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);
});
});
}

View file

@ -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'));
});
}

View file

@ -4,9 +4,10 @@
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
import { FtrConfigProviderContext } from '@kbn/test/types/ftr';
import { services } from './services'; import { services } from './services';
export async function getApiIntegrationConfig({ readConfigFile }) { export async function getApiIntegrationConfig({ readConfigFile }: FtrConfigProviderContext) {
const xPackFunctionalTestsConfig = await readConfigFile( const xPackFunctionalTestsConfig = await readConfigFile(
require.resolve('../functional/config.js') require.resolve('../functional/config.js')
); );

View file

@ -4,11 +4,14 @@
* you may not use this file except in compliance with 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'; import { default as createTestConfig } from './config';
export default async function ({ readConfigFile }) { export default async function (context: FtrConfigProviderContext) {
//security APIs should function the same under a basic or trial license // security APIs should function the same under a basic or trial license
return createTestConfig({ readConfigFile }).then((config) => { return createTestConfig(context).then((config) => {
config.esTestCluster.license = 'basic'; config.esTestCluster.license = 'basic';
config.esTestCluster.serverArgs = [ config.esTestCluster.serverArgs = [
'xpack.license.self_generated.type=basic', 'xpack.license.self_generated.type=basic',

View file

@ -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;
});
}

View file

@ -17,7 +17,7 @@ export function createTestConfig(settings: Settings) {
return async ({ readConfigFile }: FtrConfigProviderContext) => { return async ({ readConfigFile }: FtrConfigProviderContext) => {
const xPackAPITestsConfig = await readConfigFile( const xPackAPITestsConfig = await readConfigFile(
require.resolve('../../api_integration/config.js') require.resolve('../../api_integration/config.ts')
); );
return { return {

View file

@ -39,7 +39,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
return async ({ readConfigFile }: FtrConfigProviderContext) => { return async ({ readConfigFile }: FtrConfigProviderContext) => {
const xPackApiIntegrationTestsConfig = await readConfigFile( const xPackApiIntegrationTestsConfig = await readConfigFile(
require.resolve('../../api_integration/config.js') require.resolve('../../api_integration/config.ts')
); );
const servers = { const servers = {

View file

@ -42,7 +42,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
return async ({ readConfigFile }: FtrConfigProviderContext) => { return async ({ readConfigFile }: FtrConfigProviderContext) => {
const xPackApiIntegrationTestsConfig = await readConfigFile( const xPackApiIntegrationTestsConfig = await readConfigFile(
require.resolve('../../api_integration/config.js') require.resolve('../../api_integration/config.ts')
); );
const servers = { const servers = {
...xPackApiIntegrationTestsConfig.get('servers'), ...xPackApiIntegrationTestsConfig.get('servers'),

View file

@ -9,7 +9,7 @@ import { FtrConfigProviderContext } from '@kbn/test/types/ftr';
import { services } from './services'; import { services } from './services';
export default async function ({ readConfigFile }: FtrConfigProviderContext) { 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 { return {
testFiles: [require.resolve('./tests')], testFiles: [require.resolve('./tests')],

View file

@ -7,7 +7,7 @@
import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import { FtrConfigProviderContext } from '@kbn/test/types/ftr';
export default async function ({ readConfigFile }: FtrConfigProviderContext) { 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 { return {
...xPackAPITestsConfig.getAll(), ...xPackAPITestsConfig.getAll(),

View file

@ -7,7 +7,7 @@
import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import { FtrConfigProviderContext } from '@kbn/test/types/ftr';
export default async function ({ readConfigFile }: FtrConfigProviderContext) { 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 { return {
testFiles: [require.resolve('./apis')], testFiles: [require.resolve('./apis')],

View file

@ -9,7 +9,7 @@ import { FtrConfigProviderContext } from '@kbn/test/types/ftr';
import { services } from './services'; import { services } from './services';
export default async function ({ readConfigFile }: FtrConfigProviderContext) { 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( const kerberosKeytabPath = resolve(
__dirname, __dirname,

View file

@ -12,7 +12,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const kibanaAPITestsConfig = await readConfigFile( const kibanaAPITestsConfig = await readConfigFile(
require.resolve('../../../test/api_integration/config.js') 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 kibanaPort = xPackAPITestsConfig.get('servers.kibana.port');
const kerberosKeytabPath = resolve(__dirname, '../kerberos_api_integration/fixtures/krb5.keytab'); const kerberosKeytabPath = resolve(__dirname, '../kerberos_api_integration/fixtures/krb5.keytab');

View file

@ -17,7 +17,7 @@ export function createTestConfig(settings: Settings) {
return async ({ readConfigFile }: FtrConfigProviderContext) => { return async ({ readConfigFile }: FtrConfigProviderContext) => {
const xPackAPITestsConfig = await readConfigFile( const xPackAPITestsConfig = await readConfigFile(
require.resolve('../../api_integration/config.js') require.resolve('../../api_integration/config.ts')
); );
return { return {

View file

@ -9,7 +9,7 @@ import { FtrConfigProviderContext } from '@kbn/test/types/ftr';
import { services } from './services'; import { services } from './services';
export default async function ({ readConfigFile }: FtrConfigProviderContext) { 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 plugin = resolve(__dirname, './fixtures/oidc_provider');
const kibanaPort = xPackAPITestsConfig.get('servers.kibana.port'); const kibanaPort = xPackAPITestsConfig.get('servers.kibana.port');
const jwksPath = resolve(__dirname, './fixtures/jwks.json'); const jwksPath = resolve(__dirname, './fixtures/jwks.json');

View file

@ -10,7 +10,7 @@ import { CA_CERT_PATH, KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils';
import { services } from './services'; import { services } from './services';
export default async function ({ readConfigFile }: FtrConfigProviderContext) { 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 = { const servers = {
...xPackAPITestsConfig.get('servers'), ...xPackAPITestsConfig.get('servers'),

View file

@ -11,7 +11,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const kibanaAPITestsConfig = await readConfigFile( const kibanaAPITestsConfig = await readConfigFile(
require.resolve('../../../test/api_integration/config.js') 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 kibanaPort = xPackAPITestsConfig.get('servers.kibana.port');
const idpPath = resolve(__dirname, '../../test/saml_api_integration/fixtures/idp_metadata.xml'); const idpPath = resolve(__dirname, '../../test/saml_api_integration/fixtures/idp_metadata.xml');

View file

@ -24,7 +24,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
functional: await readConfigFile(require.resolve('../../../../test/functional/config.js')), functional: await readConfigFile(require.resolve('../../../../test/functional/config.js')),
}, },
xpack: { xpack: {
api: await readConfigFile(require.resolve('../../api_integration/config.js')), api: await readConfigFile(require.resolve('../../api_integration/config.ts')),
}, },
}; };

View file

@ -25,7 +25,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions)
functional: await readConfigFile(require.resolve('../../../../test/functional/config.js')), functional: await readConfigFile(require.resolve('../../../../test/functional/config.js')),
}, },
xpack: { xpack: {
api: await readConfigFile(require.resolve('../../api_integration/config.js')), api: await readConfigFile(require.resolve('../../api_integration/config.ts')),
}, },
}; };

View file

@ -5,7 +5,7 @@
*/ */
export default async function ({ readConfigFile }) { 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 { return {
testFiles: [require.resolve('./auth')], testFiles: [require.resolve('./auth')],

View file

@ -27,9 +27,6 @@
"plugins/xpack_main/*": [ "plugins/xpack_main/*": [
"x-pack/legacy/plugins/xpack_main/public/*" "x-pack/legacy/plugins/xpack_main/public/*"
], ],
"plugins/security/*": [
"x-pack/legacy/plugins/security/public/*"
],
"plugins/spaces/*": [ "plugins/spaces/*": [
"x-pack/legacy/plugins/spaces/public/*" "x-pack/legacy/plugins/spaces/public/*"
], ],

View file

@ -7,7 +7,6 @@
import 'hapi'; import 'hapi';
import { XPackMainPlugin } from '../legacy/plugins/xpack_main/server/xpack_main'; import { XPackMainPlugin } from '../legacy/plugins/xpack_main/server/xpack_main';
import { SecurityPlugin } from '../legacy/plugins/security';
import { ActionsPlugin, ActionsClient } from '../plugins/actions/server'; import { ActionsPlugin, ActionsClient } from '../plugins/actions/server';
import { AlertingPlugin, AlertsClient } from '../plugins/alerts/server'; import { AlertingPlugin, AlertsClient } from '../plugins/alerts/server';
import { TaskManager } from '../plugins/task_manager/server'; import { TaskManager } from '../plugins/task_manager/server';
@ -19,7 +18,6 @@ declare module 'hapi' {
} }
interface PluginProperties { interface PluginProperties {
xpack_main: XPackMainPlugin; xpack_main: XPackMainPlugin;
security?: SecurityPlugin;
actions?: ActionsPlugin; actions?: ActionsPlugin;
alerts?: AlertingPlugin; alerts?: AlertingPlugin;
task_manager?: TaskManager; task_manager?: TaskManager;