[Reporting] Use the deprecations service to advise critical config changes (#100427)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Tim Sullivan 2021-05-26 17:31:55 -07:00 committed by GitHub
parent f3c846cc4f
commit 417c06b9a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 174 additions and 16 deletions

View file

@ -45,7 +45,7 @@ describe('deprecations', () => {
const { messages } = applyReportingDeprecations({ roles: { enabled: true } });
expect(messages).toMatchInlineSnapshot(`
Array [
"\\"xpack.reporting.roles\\" is deprecated. Granting reporting privilege through a \\"reporting_user\\" role will not be supported starting in 8.0. Please set 'xpack.reporting.roles.enabled' to 'false' and grant reporting privilege to users through feature controls in Management > Security > Roles",
"\\"xpack.reporting.roles\\" is deprecated. Granting reporting privilege through a \\"reporting_user\\" role will not be supported starting in 8.0. Please set 'xpack.reporting.roles.enabled' to 'false' and grant reporting privileges to users using Kibana application privileges **Management > Security > Roles**.",
]
`);
});

View file

@ -35,8 +35,8 @@ export const config: PluginConfigDescriptor<ReportingConfigType> = {
addDeprecation({
message:
`"${fromPath}.roles" is deprecated. Granting reporting privilege through a "reporting_user" role will not be supported ` +
`starting in 8.0. Please set 'xpack.reporting.roles.enabled' to 'false' and grant reporting privilege to users ` +
`through feature controls in Management > Security > Roles`,
`starting in 8.0. Please set 'xpack.reporting.roles.enabled' to 'false' and grant reporting privileges to users ` +
`using Kibana application privileges **Management > Security > Roles**.`,
});
}
},

View file

@ -25,14 +25,14 @@ import { SecurityPluginSetup } from '../../security/server';
import { DEFAULT_SPACE_ID } from '../../spaces/common/constants';
import { SpacesPluginSetup } from '../../spaces/server';
import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server';
import { ReportingConfig } from './';
import { ReportingConfig, ReportingSetup } from './';
import { HeadlessChromiumDriverFactory } from './browsers/chromium/driver_factory';
import { ReportingConfigType } from './config';
import { checkLicense, getExportTypesRegistry, LevelLogger } from './lib';
import { screenshotsObservableFactory, ScreenshotsObservableFn } from './lib/screenshots';
import { ReportingStore } from './lib/store';
import { ExecuteReportTask, MonitorReportsTask, ReportTaskParams } from './lib/tasks';
import { ReportingPluginRouter, ReportingStart } from './types';
import { ReportingPluginRouter } from './types';
export interface ReportingInternalSetup {
basePath: Pick<BasePath, 'set'>;
@ -69,7 +69,7 @@ export class ReportingCore {
private config?: ReportingConfig; // final config, includes dynamic values based on OS type
private executing: Set<string>;
public getStartContract: () => ReportingStart;
public getContract: () => ReportingSetup;
constructor(private logger: LevelLogger, context: PluginInitializerContext<ReportingConfigType>) {
const syncConfig = context.config.get<ReportingConfigType>();
@ -77,11 +77,9 @@ export class ReportingCore {
this.executeTask = new ExecuteReportTask(this, syncConfig, this.logger);
this.monitorTask = new MonitorReportsTask(this, syncConfig, this.logger);
this.getStartContract = (): ReportingStart => {
return {
usesUiCapabilities: () => syncConfig.roles.enabled === false,
};
};
this.getContract = () => ({
usesUiCapabilities: () => syncConfig.roles.enabled === false,
});
this.executing = new Set();
}

View file

@ -0,0 +1,107 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ReportingCore } from '.';
import { registerDeprecations } from './deprecations';
import { createMockConfigSchema, createMockReportingCore } from './test_helpers';
import { coreMock, elasticsearchServiceMock } from 'src/core/server/mocks';
import { GetDeprecationsContext, IScopedClusterClient } from 'kibana/server';
let reportingCore: ReportingCore;
let context: GetDeprecationsContext;
let esClient: jest.Mocked<IScopedClusterClient>;
beforeEach(async () => {
const mockReportingConfig = createMockConfigSchema({ roles: { enabled: false } });
reportingCore = await createMockReportingCore(mockReportingConfig);
esClient = elasticsearchServiceMock.createScopedClusterClient();
esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({
body: { xyz: { username: 'normal_user', roles: ['data_analyst'] } },
});
context = ({ esClient } as unknown) as GetDeprecationsContext;
});
test('logs no deprecations when setup has no issues', async () => {
const { getDeprecations } = await registerDeprecations(reportingCore, coreMock.createSetup());
expect(await getDeprecations(context)).toMatchInlineSnapshot(`Array []`);
});
test('logs a plain message when only a reporting_user role issue is found', async () => {
esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({
body: { reportron: { username: 'reportron', roles: ['kibana_admin', 'reporting_user'] } },
});
const { getDeprecations } = await registerDeprecations(reportingCore, coreMock.createSetup());
expect(await getDeprecations(context)).toMatchInlineSnapshot(`
Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"Create one or more custom roles that provide Kibana application privileges to reporting features in **Management > Security > Roles**.",
"Assign the custom role(s) as desired, and remove the \\"reporting_user\\" role from the user(s).",
],
},
"documentationUrl": "https://www.elastic.co/guide/en/kibana/current/secure-reporting.html",
"level": "critical",
"message": "The deprecated \\"reporting_user\\" role has been found for 1 user(s): \\"reportron\\"",
},
]
`);
});
test('logs multiple entries when multiple reporting_user role issues are found', async () => {
esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({
body: {
reportron: { username: 'reportron', roles: ['kibana_admin', 'reporting_user'] },
supercooluser: { username: 'supercooluser', roles: ['kibana_admin', 'reporting_user'] },
},
});
const { getDeprecations } = await registerDeprecations(reportingCore, coreMock.createSetup());
expect(await getDeprecations(context)).toMatchInlineSnapshot(`
Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"Create one or more custom roles that provide Kibana application privileges to reporting features in **Management > Security > Roles**.",
"Assign the custom role(s) as desired, and remove the \\"reporting_user\\" role from the user(s).",
],
},
"documentationUrl": "https://www.elastic.co/guide/en/kibana/current/secure-reporting.html",
"level": "critical",
"message": "The deprecated \\"reporting_user\\" role has been found for 2 user(s): \\"reportron\\", \\"supercooluser\\"",
},
]
`);
});
test('logs an expanded message when a config issue and a reporting_user role issue is found', async () => {
esClient.asCurrentUser.security.getUser = jest.fn().mockResolvedValue({
body: { reportron: { username: 'reportron', roles: ['kibana_admin', 'reporting_user'] } },
});
const mockReportingConfig = createMockConfigSchema({ roles: { enabled: true } });
reportingCore = await createMockReportingCore(mockReportingConfig);
const { getDeprecations } = await registerDeprecations(reportingCore, coreMock.createSetup());
expect(await getDeprecations(context)).toMatchInlineSnapshot(`
Array [
Object {
"correctiveActions": Object {
"manualSteps": Array [
"Set \\"xpack.reporting.roles.enabled: false\\" in kibana.yml",
"Create one or more custom roles that provide Kibana application privileges to reporting features in **Management > Security > Roles**.",
"Assign the custom role(s) as desired, and remove the \\"reporting_user\\" role from the user(s).",
],
},
"documentationUrl": "https://www.elastic.co/guide/en/kibana/current/secure-reporting.html",
"level": "critical",
"message": "The deprecated \\"reporting_user\\" role has been found for 1 user(s): \\"reportron\\"",
},
]
`);
});

View file

@ -0,0 +1,52 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { CoreSetup, DeprecationsDetails, RegisterDeprecationsConfig } from 'src/core/server';
import { ReportingCore } from '.';
const deprecatedRole = 'reporting_user';
const upgradableConfig = 'xpack.reporting.roles.enabled: false';
export async function registerDeprecations(
reporting: ReportingCore,
{ deprecations: deprecationsService }: CoreSetup
) {
const deprecationsConfig: RegisterDeprecationsConfig = {
getDeprecations: async ({ esClient }) => {
const usingDeprecatedConfig = !reporting.getContract().usesUiCapabilities();
const deprecations: DeprecationsDetails[] = [];
const { body: users } = await esClient.asCurrentUser.security.getUser();
const reportingUsers = Object.entries(users)
.filter(([username, user]) => user.roles.includes(deprecatedRole))
.map(([, user]) => user.username);
const numReportingUsers = reportingUsers.length;
if (numReportingUsers > 0) {
const usernames = reportingUsers.join('", "');
deprecations.push({
message: `The deprecated "${deprecatedRole}" role has been found for ${numReportingUsers} user(s): "${usernames}"`,
documentationUrl: 'https://www.elastic.co/guide/en/kibana/current/secure-reporting.html',
level: 'critical',
correctiveActions: {
manualSteps: [
...(usingDeprecatedConfig ? [`Set "${upgradableConfig}" in kibana.yml`] : []),
`Create one or more custom roles that provide Kibana application privileges to reporting features in **Management > Security > Roles**.`,
`Assign the custom role(s) as desired, and remove the "${deprecatedRole}" role from the user(s).`,
],
},
});
}
return deprecations;
},
};
deprecationsService.registerDeprecations(deprecationsConfig);
return deprecationsConfig;
}

View file

@ -10,6 +10,7 @@ import { PLUGIN_ID } from '../common/constants';
import { ReportingCore } from './';
import { initializeBrowserDriverFactory } from './browsers';
import { buildConfig, registerUiSettings, ReportingConfigType } from './config';
import { registerDeprecations } from './deprecations';
import { LevelLogger, ReportingStore } from './lib';
import { registerRoutes } from './routes';
import { setFieldFormats } from './services';
@ -38,15 +39,13 @@ export class ReportingPlugin
// @ts-expect-error null is not assignable to object. use a boolean property to ensure reporting API is enabled.
core.http.registerRouteHandlerContext(PLUGIN_ID, () => {
if (reportingCore.pluginIsStarted()) {
return reportingCore.getStartContract();
return reportingCore.getContract();
} else {
this.logger.error(`Reporting features are not yet ready`);
return null;
}
});
registerUiSettings(core);
const { http } = core;
const { screenshotMode, features, licensing, security, spaces, taskManager } = plugins;
@ -65,6 +64,8 @@ export class ReportingPlugin
logger: this.logger,
});
registerUiSettings(core);
registerDeprecations(reportingCore, core);
registerReportingUsageCollector(reportingCore, plugins);
registerRoutes(reportingCore, this.logger);
@ -81,7 +82,7 @@ export class ReportingPlugin
});
this.reportingCore = reportingCore;
return reportingCore.getStartContract();
return reportingCore.getContract();
}
public start(core: CoreStart, plugins: ReportingStartDeps) {
@ -116,6 +117,6 @@ export class ReportingPlugin
this.logger.error(e);
});
return reportingCore.getStartContract();
return reportingCore.getContract();
}
}