diff --git a/x-pack/plugins/reporting/server/config/index.test.ts b/x-pack/plugins/reporting/server/config/index.test.ts index 8f13fe8b53810..327b03d679cae 100644 --- a/x-pack/plugins/reporting/server/config/index.test.ts +++ b/x-pack/plugins/reporting/server/config/index.test.ts @@ -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**.", ] `); }); diff --git a/x-pack/plugins/reporting/server/config/index.ts b/x-pack/plugins/reporting/server/config/index.ts index 10d7ba5059f83..8927bd8ee94d5 100644 --- a/x-pack/plugins/reporting/server/config/index.ts +++ b/x-pack/plugins/reporting/server/config/index.ts @@ -35,8 +35,8 @@ export const config: PluginConfigDescriptor = { 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**.`, }); } }, diff --git a/x-pack/plugins/reporting/server/core.ts b/x-pack/plugins/reporting/server/core.ts index 9243f9ec90f73..f996ca2a12614 100644 --- a/x-pack/plugins/reporting/server/core.ts +++ b/x-pack/plugins/reporting/server/core.ts @@ -24,14 +24,14 @@ import { LicensingPluginSetup } from '../../licensing/server'; import { SecurityPluginSetup } from '../../security/server'; import { DEFAULT_SPACE_ID } from '../../spaces/common/constants'; import { SpacesPluginSetup } from '../../spaces/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 { ESQueueInstance } from './lib/create_queue'; import { screenshotsObservableFactory, ScreenshotsObservableFn } from './lib/screenshots'; import { ReportingStore } from './lib/store'; -import { ReportingPluginRouter, ReportingStart } from './types'; +import { ReportingPluginRouter } from './types'; export interface ReportingInternalSetup { basePath: Pick; @@ -64,17 +64,15 @@ export class ReportingCore { private exportTypesRegistry = getExportTypesRegistry(); private config?: ReportingConfig; - public getStartContract: () => ReportingStart; + public getContract: () => ReportingSetup; constructor(private logger: LevelLogger, context: PluginInitializerContext) { const syncConfig = context.config.get(); this.deprecatedAllowedRoles = syncConfig.roles.enabled ? syncConfig.roles.allow : false; - this.getStartContract = (): ReportingStart => { - return { - usesUiCapabilities: () => syncConfig.roles.enabled === false, - }; - }; + this.getContract = () => ({ + usesUiCapabilities: () => syncConfig.roles.enabled === false, + }); } /* diff --git a/x-pack/plugins/reporting/server/deprecations.test.ts b/x-pack/plugins/reporting/server/deprecations.test.ts new file mode 100644 index 0000000000000..cce4721b941a0 --- /dev/null +++ b/x-pack/plugins/reporting/server/deprecations.test.ts @@ -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; + +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\\"", + }, + ] + `); +}); diff --git a/x-pack/plugins/reporting/server/deprecations.ts b/x-pack/plugins/reporting/server/deprecations.ts new file mode 100644 index 0000000000000..61074fff012a2 --- /dev/null +++ b/x-pack/plugins/reporting/server/deprecations.ts @@ -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; +} diff --git a/x-pack/plugins/reporting/server/plugin.ts b/x-pack/plugins/reporting/server/plugin.ts index 189b26b8f3f6a..34fe2e5e26ed5 100644 --- a/x-pack/plugins/reporting/server/plugin.ts +++ b/x-pack/plugins/reporting/server/plugin.ts @@ -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 { createQueueFactory, 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 } = plugins; @@ -64,6 +63,8 @@ export class ReportingPlugin logger: this.logger, }); + registerUiSettings(core); + registerDeprecations(reportingCore, core); registerReportingUsageCollector(reportingCore, plugins); registerRoutes(reportingCore, this.logger); @@ -80,7 +81,7 @@ export class ReportingPlugin }); this.reportingCore = reportingCore; - return reportingCore.getStartContract(); + return reportingCore.getContract(); } public start(core: CoreStart, plugins: ReportingStartDeps) { @@ -121,6 +122,6 @@ export class ReportingPlugin this.logger.error(e); }); - return reportingCore.getStartContract(); + return reportingCore.getContract(); } }