From 279c622960db9b018ac1e7cd473716bc7dd6af9e Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Tue, 9 Feb 2021 13:10:10 -0500 Subject: [PATCH 1/3] init commit --- src/plugins/deprecations/README.md | 19 +++ src/plugins/deprecations/common/index.ts | 2 + src/plugins/deprecations/jest.config.ts | 13 ++ src/plugins/deprecations/kibana.json | 9 ++ .../deprecations/public/application.tsx | 31 +++++ .../deprecations/public/components/app.tsx | 124 ++++++++++++++++++ src/plugins/deprecations/public/index.scss | 0 src/plugins/deprecations/public/index.ts | 10 ++ src/plugins/deprecations/public/plugin.ts | 45 +++++++ src/plugins/deprecations/public/types.ts | 11 ++ .../deprecations/server/deprecations.ts | 39 ++++++ src/plugins/deprecations/server/index.ts | 13 ++ src/plugins/deprecations/server/mocks.ts | 7 + src/plugins/deprecations/server/plugin.ts | 43 ++++++ .../server/routes/deprecations.ts | 40 ++++++ .../deprecations/server/routes/index.ts | 14 ++ src/plugins/deprecations/server/types.ts | 4 + src/plugins/deprecations/tsconfig.json | 17 +++ src/plugins/timelion/kibana.json | 3 +- src/plugins/timelion/server/deprecations.ts | 42 ++++++ src/plugins/timelion/server/plugin.ts | 28 ++-- x-pack/plugins/security/kibana.json | 2 +- .../server/deprecations/deprecations.ts | 91 +++++++++++++ .../security/server/deprecations/index.ts | 8 ++ x-pack/plugins/security/server/plugin.ts | 9 ++ .../server/routes/deprecations/get.ts | 89 +++++++++++++ .../server/routes/deprecations/index.ts | 13 ++ .../plugins/security/server/routes/index.ts | 2 + 28 files changed, 711 insertions(+), 17 deletions(-) create mode 100755 src/plugins/deprecations/README.md create mode 100644 src/plugins/deprecations/common/index.ts create mode 100644 src/plugins/deprecations/jest.config.ts create mode 100644 src/plugins/deprecations/kibana.json create mode 100644 src/plugins/deprecations/public/application.tsx create mode 100644 src/plugins/deprecations/public/components/app.tsx create mode 100644 src/plugins/deprecations/public/index.scss create mode 100644 src/plugins/deprecations/public/index.ts create mode 100644 src/plugins/deprecations/public/plugin.ts create mode 100644 src/plugins/deprecations/public/types.ts create mode 100644 src/plugins/deprecations/server/deprecations.ts create mode 100644 src/plugins/deprecations/server/index.ts create mode 100644 src/plugins/deprecations/server/mocks.ts create mode 100644 src/plugins/deprecations/server/plugin.ts create mode 100644 src/plugins/deprecations/server/routes/deprecations.ts create mode 100644 src/plugins/deprecations/server/routes/index.ts create mode 100644 src/plugins/deprecations/server/types.ts create mode 100644 src/plugins/deprecations/tsconfig.json create mode 100644 src/plugins/timelion/server/deprecations.ts create mode 100644 x-pack/plugins/security/server/deprecations/deprecations.ts create mode 100644 x-pack/plugins/security/server/deprecations/index.ts create mode 100644 x-pack/plugins/security/server/routes/deprecations/get.ts create mode 100644 x-pack/plugins/security/server/routes/deprecations/index.ts diff --git a/src/plugins/deprecations/README.md b/src/plugins/deprecations/README.md new file mode 100755 index 0000000000000..1b0996da50372 --- /dev/null +++ b/src/plugins/deprecations/README.md @@ -0,0 +1,19 @@ +# deprecations + +A Kibana plugin + +--- + +## Development + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment. + +## Scripts + +
+
yarn kbn bootstrap
+
Execute this to install node_modules and setup the dependencies in your plugin and in Kibana
+ +
yarn plugin-helpers build
+
Execute this to create a distributable version of this plugin that can be installed in Kibana
+
diff --git a/src/plugins/deprecations/common/index.ts b/src/plugins/deprecations/common/index.ts new file mode 100644 index 0000000000000..2866754dbc633 --- /dev/null +++ b/src/plugins/deprecations/common/index.ts @@ -0,0 +1,2 @@ +export const PLUGIN_ID = 'deprecations'; +export const PLUGIN_NAME = 'deprecations'; diff --git a/src/plugins/deprecations/jest.config.ts b/src/plugins/deprecations/jest.config.ts new file mode 100644 index 0000000000000..3f4826387afa7 --- /dev/null +++ b/src/plugins/deprecations/jest.config.ts @@ -0,0 +1,13 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/deprecations'], +}; diff --git a/src/plugins/deprecations/kibana.json b/src/plugins/deprecations/kibana.json new file mode 100644 index 0000000000000..46505197d1c32 --- /dev/null +++ b/src/plugins/deprecations/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "deprecations", + "version": "1.0.0", + "kibanaVersion": "kibana", + "server": true, + "ui": true, + "requiredPlugins": ["navigation"], + "optionalPlugins": [] +} diff --git a/src/plugins/deprecations/public/application.tsx b/src/plugins/deprecations/public/application.tsx new file mode 100644 index 0000000000000..3e0cbb5d777f7 --- /dev/null +++ b/src/plugins/deprecations/public/application.tsx @@ -0,0 +1,31 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { AppMountParameters, CoreStart } from 'src/core/public'; +import { AppPluginStartDependencies } from './types'; +import { DeprecationsApp } from './components/app'; + +export const renderApp = ( + { notifications, http }: CoreStart, + { navigation }: AppPluginStartDependencies, + { appBasePath, element }: AppMountParameters +) => { + ReactDOM.render( + , + element + ); + + return () => ReactDOM.unmountComponentAtNode(element); +}; diff --git a/src/plugins/deprecations/public/components/app.tsx b/src/plugins/deprecations/public/components/app.tsx new file mode 100644 index 0000000000000..57f71b205ed52 --- /dev/null +++ b/src/plugins/deprecations/public/components/app.tsx @@ -0,0 +1,124 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; +import { BrowserRouter as Router } from 'react-router-dom'; + +import { + EuiButton, + EuiHorizontalRule, + EuiPage, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageContentHeader, + EuiPageHeader, + EuiTitle, + EuiText, +} from '@elastic/eui'; + +import { CoreStart } from 'src/core/public'; +import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; + +import { PLUGIN_ID, PLUGIN_NAME } from '../../common'; + +interface DeprecationsAppDeps { + basename: string; + notifications: CoreStart['notifications']; + http: CoreStart['http']; + navigation: NavigationPublicPluginStart; +} + +export const DeprecationsApp = ({ + basename, + notifications, + http, + navigation, +}: DeprecationsAppDeps) => { + // Use React hooks to manage state. + const [timestamp, setTimestamp] = useState(); + + const onClickHandler = () => { + // Use the core http service to make a response to the server API. + http.get('/api/deprecations/example').then((res) => { + setTimestamp(res.time); + // Use the core notifications service to display a success message. + notifications.toasts.addSuccess( + i18n.translate('deprecations.dataUpdated', { + defaultMessage: 'Data updated', + }) + ); + }); + }; + + // Render the application DOM. + // Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract. + return ( + + + <> + + + + + +

+ +

+
+
+ + + +

+ +

+
+
+ + +

+ +

+ +

+ +

+ + + +
+
+
+
+
+ +
+
+ ); +}; diff --git a/src/plugins/deprecations/public/index.scss b/src/plugins/deprecations/public/index.scss new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/plugins/deprecations/public/index.ts b/src/plugins/deprecations/public/index.ts new file mode 100644 index 0000000000000..3c8dc7e42ed51 --- /dev/null +++ b/src/plugins/deprecations/public/index.ts @@ -0,0 +1,10 @@ +import './index.scss'; + +import { DeprecationsPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new DeprecationsPlugin(); +} +export { DeprecationsPluginSetup, DeprecationsPluginStart } from './types'; diff --git a/src/plugins/deprecations/public/plugin.ts b/src/plugins/deprecations/public/plugin.ts new file mode 100644 index 0000000000000..b2967d94425e2 --- /dev/null +++ b/src/plugins/deprecations/public/plugin.ts @@ -0,0 +1,45 @@ +import { i18n } from '@kbn/i18n'; +import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '../../../src/core/public'; +import { + DeprecationsPluginSetup, + DeprecationsPluginStart, + AppPluginStartDependencies, +} from './types'; +import { PLUGIN_NAME } from '../common'; + +export class DeprecationsPlugin + implements Plugin { + public setup(core: CoreSetup): DeprecationsPluginSetup { + // Register an application into the side navigation menu + core.application.register({ + id: 'deprecations', + title: PLUGIN_NAME, + async mount(params: AppMountParameters) { + // Load application bundle + const { renderApp } = await import('./application'); + // Get start services as specified in kibana.json + const [coreStart, depsStart] = await core.getStartServices(); + // Render the application + return renderApp(coreStart, depsStart as AppPluginStartDependencies, params); + }, + }); + + // Return methods that should be available to other plugins + return { + getGreeting() { + return i18n.translate('deprecations.greetingText', { + defaultMessage: 'Hello from {name}!', + values: { + name: PLUGIN_NAME, + }, + }); + }, + }; + } + + public start(core: CoreStart): DeprecationsPluginStart { + return {}; + } + + public stop() {} +} diff --git a/src/plugins/deprecations/public/types.ts b/src/plugins/deprecations/public/types.ts new file mode 100644 index 0000000000000..fe7d302942cf0 --- /dev/null +++ b/src/plugins/deprecations/public/types.ts @@ -0,0 +1,11 @@ +import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public'; + +export interface DeprecationsPluginSetup { + getGreeting: () => string; +} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface DeprecationsPluginStart {} + +export interface AppPluginStartDependencies { + navigation: NavigationPublicPluginStart; +} diff --git a/src/plugins/deprecations/server/deprecations.ts b/src/plugins/deprecations/server/deprecations.ts new file mode 100644 index 0000000000000..db8c6d0618881 --- /dev/null +++ b/src/plugins/deprecations/server/deprecations.ts @@ -0,0 +1,39 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +interface DeprecationInfo { + message: string; + documentationUrl: string; + level: 'critical' | 'warning'; + correctiveAction?: () => void; +} + +interface PluginDeprecation { + pluginId: string; + getDeprecations: () => Promise; +} + +export class Deprecations { + private readonly deprecations: { [key: string]: PluginDeprecation } = {}; + + public registerDeprecations = (deprecation: PluginDeprecation) => { + if (this.deprecations[deprecation.pluginId]) { + throw new Error(`Plugin "${deprecation.pluginId}" is duplicated.`); + } + + this.deprecations[deprecation.pluginId] = deprecation; + }; + + public getDeprecationsByPluginId = (pluginId: string) => { + return this.deprecations[pluginId]; + }; + + public getDeprecations = () => { + return this.deprecations; + }; +} diff --git a/src/plugins/deprecations/server/index.ts b/src/plugins/deprecations/server/index.ts new file mode 100644 index 0000000000000..e90ec8c9a3a3b --- /dev/null +++ b/src/plugins/deprecations/server/index.ts @@ -0,0 +1,13 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PluginInitializerContext } from 'src/core/server'; +import { DeprecationsPlugin } from './plugin'; + +export const plugin = (initializerContext: PluginInitializerContext) => + new DeprecationsPlugin(initializerContext); diff --git a/src/plugins/deprecations/server/mocks.ts b/src/plugins/deprecations/server/mocks.ts new file mode 100644 index 0000000000000..5c2d5b68ae2e0 --- /dev/null +++ b/src/plugins/deprecations/server/mocks.ts @@ -0,0 +1,7 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ diff --git a/src/plugins/deprecations/server/plugin.ts b/src/plugins/deprecations/server/plugin.ts new file mode 100644 index 0000000000000..ca184725409d0 --- /dev/null +++ b/src/plugins/deprecations/server/plugin.ts @@ -0,0 +1,43 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from 'src/core/server'; + +import { DeprecationsPluginSetup, DeprecationsPluginStart } from './types'; +import { setupRoutes } from './routes'; +import { Deprecations } from './deprecations'; + +export class DeprecationsPlugin + implements Plugin { + private readonly logger: Logger; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get(); + } + + public setup(core: CoreSetup) { + this.logger.debug('deprecations: Setup'); + + const deprecations = new Deprecations(); + const router = core.http.createRouter(); + + // Register server side APIs + setupRoutes({ router, deprecations }); + + return deprecations; + } + + public start(core: CoreStart) { + this.logger.debug('deprecations: Started'); + return {}; + } + + public stop() { + this.logger.debug('Stopping plugin'); + } +} diff --git a/src/plugins/deprecations/server/routes/deprecations.ts b/src/plugins/deprecations/server/routes/deprecations.ts new file mode 100644 index 0000000000000..b9f1ae984616f --- /dev/null +++ b/src/plugins/deprecations/server/routes/deprecations.ts @@ -0,0 +1,40 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { IRouter } from 'src/core/server'; + +export function registerDeprecationRoutes(router: IRouter, deprecations: any) { + router.get( + { + path: '/api/deprecations', + validate: false, + }, + async (context, request, response) => { + const deps = deprecations.getDeprecations(); + + const esClient = context.core.elasticsearch.client; + + const allDeps = await Promise.all( + Object.entries(deps).map(async ([key, value]) => { + const pluginDeprecations = await value.getDeprecations(esClient); + const pluginDeprecationsWithId = pluginDeprecations.map((deps) => ({ + ...deps, + pluginId: key, + })); + return pluginDeprecationsWithId; + }) + ); + + return response.ok({ + body: { + deprecations: allDeps.flat(), + }, + }); + } + ); +} diff --git a/src/plugins/deprecations/server/routes/index.ts b/src/plugins/deprecations/server/routes/index.ts new file mode 100644 index 0000000000000..594a75379781b --- /dev/null +++ b/src/plugins/deprecations/server/routes/index.ts @@ -0,0 +1,14 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { IRouter } from 'src/core/server'; +import { registerDeprecationRoutes } from './deprecations'; + +export function setupRoutes({ router, deprecations }: { router: IRouter; deprecations: any }) { + registerDeprecationRoutes(router, deprecations); +} diff --git a/src/plugins/deprecations/server/types.ts b/src/plugins/deprecations/server/types.ts new file mode 100644 index 0000000000000..59c40480e93e1 --- /dev/null +++ b/src/plugins/deprecations/server/types.ts @@ -0,0 +1,4 @@ +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface DeprecationsPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface DeprecationsPluginStart {} diff --git a/src/plugins/deprecations/tsconfig.json b/src/plugins/deprecations/tsconfig.json new file mode 100644 index 0000000000000..b705b5255461f --- /dev/null +++ b/src/plugins/deprecations/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "public/**/*", + "server/**/*", + "common/*", + "../../../typings/**/*" + ], + "references": [] +} diff --git a/src/plugins/timelion/kibana.json b/src/plugins/timelion/kibana.json index 3134cc265fba1..4d6f6030204e6 100644 --- a/src/plugins/timelion/kibana.json +++ b/src/plugins/timelion/kibana.json @@ -14,6 +14,7 @@ "navigation", "visTypeTimelion", "savedObjects", - "kibanaLegacy" + "kibanaLegacy", + "deprecations" ] } diff --git a/src/plugins/timelion/server/deprecations.ts b/src/plugins/timelion/server/deprecations.ts new file mode 100644 index 0000000000000..bc7fc3b4e9256 --- /dev/null +++ b/src/plugins/timelion/server/deprecations.ts @@ -0,0 +1,42 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SavedObjectsServiceStart } from 'src/core/server'; + +export const getDeprecations = async (savedObjects: SavedObjectsServiceStart) => { + const deprecations = []; + // const savedObjectsClient = savedObjects.createInternalRepository(); + + // const getTimelionSheets = () => + // savedObjectsClient + // .find({ + // type: 'timelion-sheet', + // perPage: 1, + // }) + // .then(({ total }) => total); + + // const timelionWorksheets = await getTimelionSheets(); + + // if (timelionWorksheets > 0) { + // deprecations.push({ + // message: `You have ${timelionWorksheets} Timelion worksheets. The Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard.`, + // documentationUrl: + // 'https://www.elastic.co/guide/en/kibana/master/dashboard.html#timelion-deprecation', + // level: 'warning', + // }); + // } + + deprecations.push({ + message: `You have 1 Timelion worksheets. The Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard.`, + documentationUrl: + 'https://www.elastic.co/guide/en/kibana/master/dashboard.html#timelion-deprecation', + level: 'warning', + }); + + return deprecations; +}; diff --git a/src/plugins/timelion/server/plugin.ts b/src/plugins/timelion/server/plugin.ts index 2fb3918c17089..6e13f48583ba4 100644 --- a/src/plugins/timelion/server/plugin.ts +++ b/src/plugins/timelion/server/plugin.ts @@ -11,6 +11,7 @@ import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import { TimelionConfigType } from './config'; import { timelionSheetSavedObjectType } from './saved_objects'; +import { getDeprecations } from './deprecations'; /** * Deprecated since 7.0, the Timelion app will be removed in 8.0. @@ -18,22 +19,14 @@ import { timelionSheetSavedObjectType } from './saved_objects'; * * @link https://www.elastic.co/guide/en/kibana/master/timelion.html#timelion-deprecation **/ -const showWarningMessageIfTimelionSheetWasFound = (core: CoreStart, logger: Logger) => { - const { savedObjects } = core; - const savedObjectsClient = savedObjects.createInternalRepository(); +const showWarningMessageIfTimelionSheetWasFound = async (core: CoreStart, logger: Logger) => { + const deprecations = await getDeprecations(core.savedObjects); - savedObjectsClient - .find({ - type: 'timelion-sheet', - perPage: 1, - }) - .then( - ({ total }) => - total && - logger.warn( - 'Deprecated since 7.0, the Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard. See https://www.elastic.co/guide/en/kibana/master/dashboard.html#timelion-deprecation.' - ) + if (deprecations.length) { + logger.warn( + 'Deprecated since 7.0, the Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard. See https://www.elastic.co/guide/en/kibana/master/dashboard.html#timelion-deprecation.' ); + } }; export class TimelionPlugin implements Plugin { @@ -43,7 +36,7 @@ export class TimelionPlugin implements Plugin { this.logger = context.logger.get(); } - public setup(core: CoreSetup) { + public setup(core: CoreSetup, { deprecations }) { core.capabilities.registerProvider(() => ({ timelion: { save: true, @@ -86,6 +79,11 @@ export class TimelionPlugin implements Plugin { schema: schema.number(), }, }); + + deprecations.registerDeprecations({ + pluginId: 'timelion', + getDeprecations: getDeprecations.bind(core.savedObjects), + }); } start(core: CoreStart) { showWarningMessageIfTimelionSheetWasFound(core, this.logger); diff --git a/x-pack/plugins/security/kibana.json b/x-pack/plugins/security/kibana.json index f6e7b8bf46a39..a77fdf171f723 100644 --- a/x-pack/plugins/security/kibana.json +++ b/x-pack/plugins/security/kibana.json @@ -3,7 +3,7 @@ "version": "8.0.0", "kibanaVersion": "kibana", "configPath": ["xpack", "security"], - "requiredPlugins": ["data", "features", "licensing", "taskManager", "securityOss"], + "requiredPlugins": ["data", "features", "licensing", "taskManager", "securityOss", "deprecations"], "optionalPlugins": ["home", "management", "usageCollection", "spaces"], "server": true, "ui": true, diff --git a/x-pack/plugins/security/server/deprecations/deprecations.ts b/x-pack/plugins/security/server/deprecations/deprecations.ts new file mode 100644 index 0000000000000..b083575568bca --- /dev/null +++ b/x-pack/plugins/security/server/deprecations/deprecations.ts @@ -0,0 +1,91 @@ +/* + * 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 { ElasticsearchClient, IClusterClient, IScopedClusterClient } from 'kibana/server'; + +export interface DeprecationsServiceSetup { + getClusterClient: () => Promise; +} + +interface UserInfo { + username: string; + roles: string[]; + full_name: string | null; + email: string | null; + metadata: { + [key: string]: any; + }; + enabled: boolean; +} + +export class DeprecationsService { + private elasticsearchClient?: IClusterClient; + + private isUsingDeprecatedRole(users: Record, role: string) { + const usersWithDeprecatedRoles = Object.keys(users).filter((user) => { + return users[user].roles.includes(role); + }); + + return usersWithDeprecatedRoles.map((user) => { + const userInfo = users[user]; + return { + message: `User '${userInfo.username}' is using a deprecated role: '${role}'`, + correctiveAction: '', + documentationUrl: '', + level: 'critical', + }; + }); + } + + private isUsingDeprecatedUser(users: Record, username: string) { + const deprecatedUsers = Object.keys(users).filter((user) => { + return users[user].username === username; + }); + + return deprecatedUsers.map((user) => { + const userInfo = users[user]; + return { + message: `User '${userInfo.username}' has been deprecated.`, + correctiveAction: '', + documentationUrl: '', + level: 'critical', + }; + }); + } + + public async setup() {} + + public async getDeprecations(esClient: IScopedClusterClient) { + try { + const { body: usersResponse } = await esClient.asCurrentUser.security.getUser< + Record + >(); + + const usersWithDeprecatedKibanaUserRole = this.isUsingDeprecatedRole( + usersResponse, + 'kibana_user' + ); + const usersWithDeprecatedDashboardRole = this.isUsingDeprecatedRole( + usersResponse, + 'kibana_dashboard_only_user' + ); + + const deprecatedUsers = this.isUsingDeprecatedUser(usersResponse, 'kibana'); + + const deprecations = [ + ...usersWithDeprecatedKibanaUserRole, + ...usersWithDeprecatedDashboardRole, + ...deprecatedUsers, + ]; + + return deprecations; + } catch (error) { + // TODO handle error + console.log('error', error); + } + } +} diff --git a/x-pack/plugins/security/server/deprecations/index.ts b/x-pack/plugins/security/server/deprecations/index.ts new file mode 100644 index 0000000000000..5aeab91127d1a --- /dev/null +++ b/x-pack/plugins/security/server/deprecations/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { DeprecationsService } from './deprecations'; diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index cccfa7de6d177..e7354f3fabb2b 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -43,6 +43,7 @@ import { ElasticsearchService } from './elasticsearch'; import { Session, SessionManagementService } from './session_management'; import { registerSecurityUsageCollector } from './usage_collector'; import { setupSpacesClient } from './spaces'; +import { DeprecationsService } from './deprecations'; export type SpacesService = Pick< SpacesPluginSetup['spacesService'], @@ -88,6 +89,7 @@ export interface PluginSetupDependencies { features: FeaturesPluginSetup; licensing: LicensingPluginSetup; taskManager: TaskManagerSetupContract; + deprecations: any; usageCollection?: UsageCollectionSetup; securityOss?: SecurityOssPluginSetup; spaces?: SpacesPluginSetup; @@ -173,6 +175,7 @@ export class SecurityPlugin this.initializerContext.logger.get('anonymous-access'), this.getConfig ); + private readonly deprecationsService = new DeprecationsService(); constructor(private readonly initializerContext: PluginInitializerContext) { this.logger = this.initializerContext.logger.get(); @@ -187,6 +190,7 @@ export class SecurityPlugin usageCollection, securityOss, spaces, + deprecations, }: PluginSetupDependencies ) { this.configSubscription = combineLatest([ @@ -298,6 +302,11 @@ export class SecurityPlugin getAuthenticationService: this.getAuthentication, }); + deprecations.registerDeprecations({ + pluginId: 'security', + getDeprecations: this.deprecationsService.getDeprecations.bind(this.deprecationsService), + }); + return Object.freeze({ audit: { asScoped: this.auditSetup.asScoped, diff --git a/x-pack/plugins/security/server/routes/deprecations/get.ts b/x-pack/plugins/security/server/routes/deprecations/get.ts new file mode 100644 index 0000000000000..9e6373af8ef86 --- /dev/null +++ b/x-pack/plugins/security/server/routes/deprecations/get.ts @@ -0,0 +1,89 @@ +/* + * 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 { createLicensedRouteHandler } from '../licensed_route_handler'; +import { wrapError } from '../../errors'; +import { RouteDefinitionParams } from '..'; + +interface UserInfo { + username: string; + roles: string[]; + full_name: string | null; + email: string | null; + metadata: { + [key: string]: any; + }; + enabled: boolean; +} + +const isUsingDeprecatedRole = (users: Record, role: string) => { + const usersWithDeprecatedRoles = Object.keys(users).filter((user) => { + return users[user].roles.includes(role); + }); + + return usersWithDeprecatedRoles.map((user) => { + const userInfo = users[user]; + return { + message: `User '${userInfo.username}' is using a deprecated role: '${role}'`, + correctiveAction: '', + documentationUrl: '', + level: 'critical', + }; + }); +}; + +const isUsingDeprecatedUser = (users: Record, username: string) => { + const deprecatedUsers = Object.keys(users).filter((user) => { + return users[user].username === username; + }); + + return deprecatedUsers.map((user) => { + const userInfo = users[user]; + return { + message: `User '${userInfo.username}' has been deprecated.`, + correctiveAction: '', + documentationUrl: '', + level: 'critical', + }; + }); +}; + +export function defineDeprecationsGetRoutes(params: RouteDefinitionParams) { + const { logger, router } = params; + + router.get( + { + path: '/internal/security/deprecations', + validate: false, + }, + createLicensedRouteHandler(async (context, request, response) => { + try { + const { + body: usersResponse, + } = await context.core.elasticsearch.client.asCurrentUser.security.getUser< + Record + >(); + + const usersWithDeprecatedRoles = [ + ...isUsingDeprecatedRole(usersResponse, 'kibana_user'), + ...isUsingDeprecatedRole(usersResponse, 'kibana_dashboard_only_user'), + ]; + const deprecatedUsers = isUsingDeprecatedUser(usersResponse, 'kibana'); + + const deprecations = [...usersWithDeprecatedRoles, ...deprecatedUsers]; + + return response.ok({ body: deprecations }); + } catch (error) { + const wrappedError = wrapError(error); + return response.customError({ + body: wrappedError, + statusCode: wrappedError.output.statusCode, + }); + } + }) + ); +} diff --git a/x-pack/plugins/security/server/routes/deprecations/index.ts b/x-pack/plugins/security/server/routes/deprecations/index.ts new file mode 100644 index 0000000000000..0a7a7337defc2 --- /dev/null +++ b/x-pack/plugins/security/server/routes/deprecations/index.ts @@ -0,0 +1,13 @@ +/* + * 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 { defineDeprecationsGetRoutes } from './get'; +import { RouteDefinitionParams } from '..'; + +export function defineDeprecationRoutes(params: RouteDefinitionParams) { + defineDeprecationsGetRoutes(params); +} diff --git a/x-pack/plugins/security/server/routes/index.ts b/x-pack/plugins/security/server/routes/index.ts index ba59d0f39692b..073d17b83438e 100644 --- a/x-pack/plugins/security/server/routes/index.ts +++ b/x-pack/plugins/security/server/routes/index.ts @@ -24,6 +24,7 @@ import { defineUsersRoutes } from './users'; import { defineRoleMappingRoutes } from './role_mapping'; import { defineSessionManagementRoutes } from './session_management'; import { defineViewRoutes } from './views'; +import { defineDeprecationRoutes } from './deprecations'; /** * Describes parameters used to define HTTP routes. @@ -51,4 +52,5 @@ export function defineRoutes(params: RouteDefinitionParams) { defineUsersRoutes(params); defineRoleMappingRoutes(params); defineViewRoutes(params); + defineDeprecationRoutes(params); } From a4739468e69091fc5d499540d353411d672199e3 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Wed, 10 Feb 2021 08:56:09 -0500 Subject: [PATCH 2/3] cleanup --- src/plugins/deprecations/common/index.ts | 8 ++ src/plugins/deprecations/kibana.json | 4 +- .../deprecations/public/application.tsx | 31 ----- .../deprecations/public/components/app.tsx | 124 ------------------ src/plugins/deprecations/public/index.scss | 0 src/plugins/deprecations/public/index.ts | 10 -- src/plugins/deprecations/public/plugin.ts | 45 ------- src/plugins/deprecations/public/types.ts | 11 -- .../deprecations/server/deprecations.ts | 20 +-- src/plugins/deprecations/server/index.ts | 2 + .../server/routes/deprecations.ts | 35 +++-- .../deprecations/server/routes/index.ts | 2 + src/plugins/deprecations/server/types.ts | 27 ++++ src/plugins/deprecations/tsconfig.json | 6 +- src/plugins/timelion/server/deprecations.ts | 44 +++---- src/plugins/timelion/server/plugin.ts | 10 +- src/plugins/timelion/tsconfig.json | 3 + .../server/deprecations/deprecations.ts | 114 +++++++--------- .../security/server/deprecations/index.ts | 2 +- x-pack/plugins/security/server/plugin.ts | 7 +- .../server/routes/deprecations/get.ts | 89 ------------- .../server/routes/deprecations/index.ts | 13 -- .../plugins/security/server/routes/index.ts | 2 - x-pack/plugins/security/tsconfig.json | 5 +- 24 files changed, 164 insertions(+), 450 deletions(-) delete mode 100644 src/plugins/deprecations/public/application.tsx delete mode 100644 src/plugins/deprecations/public/components/app.tsx delete mode 100644 src/plugins/deprecations/public/index.scss delete mode 100644 src/plugins/deprecations/public/index.ts delete mode 100644 src/plugins/deprecations/public/plugin.ts delete mode 100644 src/plugins/deprecations/public/types.ts delete mode 100644 x-pack/plugins/security/server/routes/deprecations/get.ts delete mode 100644 x-pack/plugins/security/server/routes/deprecations/index.ts diff --git a/src/plugins/deprecations/common/index.ts b/src/plugins/deprecations/common/index.ts index 2866754dbc633..30c6195121de4 100644 --- a/src/plugins/deprecations/common/index.ts +++ b/src/plugins/deprecations/common/index.ts @@ -1,2 +1,10 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + export const PLUGIN_ID = 'deprecations'; export const PLUGIN_NAME = 'deprecations'; diff --git a/src/plugins/deprecations/kibana.json b/src/plugins/deprecations/kibana.json index 46505197d1c32..dcc33157d0f64 100644 --- a/src/plugins/deprecations/kibana.json +++ b/src/plugins/deprecations/kibana.json @@ -3,7 +3,7 @@ "version": "1.0.0", "kibanaVersion": "kibana", "server": true, - "ui": true, - "requiredPlugins": ["navigation"], + "ui": false, + "requiredPlugins": [], "optionalPlugins": [] } diff --git a/src/plugins/deprecations/public/application.tsx b/src/plugins/deprecations/public/application.tsx deleted file mode 100644 index 3e0cbb5d777f7..0000000000000 --- a/src/plugins/deprecations/public/application.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import ReactDOM from 'react-dom'; -import { AppMountParameters, CoreStart } from 'src/core/public'; -import { AppPluginStartDependencies } from './types'; -import { DeprecationsApp } from './components/app'; - -export const renderApp = ( - { notifications, http }: CoreStart, - { navigation }: AppPluginStartDependencies, - { appBasePath, element }: AppMountParameters -) => { - ReactDOM.render( - , - element - ); - - return () => ReactDOM.unmountComponentAtNode(element); -}; diff --git a/src/plugins/deprecations/public/components/app.tsx b/src/plugins/deprecations/public/components/app.tsx deleted file mode 100644 index 57f71b205ed52..0000000000000 --- a/src/plugins/deprecations/public/components/app.tsx +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React, { useState } from 'react'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; -import { BrowserRouter as Router } from 'react-router-dom'; - -import { - EuiButton, - EuiHorizontalRule, - EuiPage, - EuiPageBody, - EuiPageContent, - EuiPageContentBody, - EuiPageContentHeader, - EuiPageHeader, - EuiTitle, - EuiText, -} from '@elastic/eui'; - -import { CoreStart } from 'src/core/public'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; - -import { PLUGIN_ID, PLUGIN_NAME } from '../../common'; - -interface DeprecationsAppDeps { - basename: string; - notifications: CoreStart['notifications']; - http: CoreStart['http']; - navigation: NavigationPublicPluginStart; -} - -export const DeprecationsApp = ({ - basename, - notifications, - http, - navigation, -}: DeprecationsAppDeps) => { - // Use React hooks to manage state. - const [timestamp, setTimestamp] = useState(); - - const onClickHandler = () => { - // Use the core http service to make a response to the server API. - http.get('/api/deprecations/example').then((res) => { - setTimestamp(res.time); - // Use the core notifications service to display a success message. - notifications.toasts.addSuccess( - i18n.translate('deprecations.dataUpdated', { - defaultMessage: 'Data updated', - }) - ); - }); - }; - - // Render the application DOM. - // Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract. - return ( - - - <> - - - - - -

- -

-
-
- - - -

- -

-
-
- - -

- -

- -

- -

- - - -
-
-
-
-
- -
-
- ); -}; diff --git a/src/plugins/deprecations/public/index.scss b/src/plugins/deprecations/public/index.scss deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/src/plugins/deprecations/public/index.ts b/src/plugins/deprecations/public/index.ts deleted file mode 100644 index 3c8dc7e42ed51..0000000000000 --- a/src/plugins/deprecations/public/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import './index.scss'; - -import { DeprecationsPlugin } from './plugin'; - -// This exports static code and TypeScript types, -// as well as, Kibana Platform `plugin()` initializer. -export function plugin() { - return new DeprecationsPlugin(); -} -export { DeprecationsPluginSetup, DeprecationsPluginStart } from './types'; diff --git a/src/plugins/deprecations/public/plugin.ts b/src/plugins/deprecations/public/plugin.ts deleted file mode 100644 index b2967d94425e2..0000000000000 --- a/src/plugins/deprecations/public/plugin.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { i18n } from '@kbn/i18n'; -import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '../../../src/core/public'; -import { - DeprecationsPluginSetup, - DeprecationsPluginStart, - AppPluginStartDependencies, -} from './types'; -import { PLUGIN_NAME } from '../common'; - -export class DeprecationsPlugin - implements Plugin { - public setup(core: CoreSetup): DeprecationsPluginSetup { - // Register an application into the side navigation menu - core.application.register({ - id: 'deprecations', - title: PLUGIN_NAME, - async mount(params: AppMountParameters) { - // Load application bundle - const { renderApp } = await import('./application'); - // Get start services as specified in kibana.json - const [coreStart, depsStart] = await core.getStartServices(); - // Render the application - return renderApp(coreStart, depsStart as AppPluginStartDependencies, params); - }, - }); - - // Return methods that should be available to other plugins - return { - getGreeting() { - return i18n.translate('deprecations.greetingText', { - defaultMessage: 'Hello from {name}!', - values: { - name: PLUGIN_NAME, - }, - }); - }, - }; - } - - public start(core: CoreStart): DeprecationsPluginStart { - return {}; - } - - public stop() {} -} diff --git a/src/plugins/deprecations/public/types.ts b/src/plugins/deprecations/public/types.ts deleted file mode 100644 index fe7d302942cf0..0000000000000 --- a/src/plugins/deprecations/public/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public'; - -export interface DeprecationsPluginSetup { - getGreeting: () => string; -} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DeprecationsPluginStart {} - -export interface AppPluginStartDependencies { - navigation: NavigationPublicPluginStart; -} diff --git a/src/plugins/deprecations/server/deprecations.ts b/src/plugins/deprecations/server/deprecations.ts index db8c6d0618881..b8d0793531835 100644 --- a/src/plugins/deprecations/server/deprecations.ts +++ b/src/plugins/deprecations/server/deprecations.ts @@ -6,22 +6,12 @@ * Side Public License, v 1. */ -interface DeprecationInfo { - message: string; - documentationUrl: string; - level: 'critical' | 'warning'; - correctiveAction?: () => void; -} - -interface PluginDeprecation { - pluginId: string; - getDeprecations: () => Promise; -} +import { DeprecationInfo, DeprecationContext } from './types'; export class Deprecations { - private readonly deprecations: { [key: string]: PluginDeprecation } = {}; + private readonly deprecations: { [key: string]: DeprecationContext } = {}; - public registerDeprecations = (deprecation: PluginDeprecation) => { + public registerDeprecations = (deprecation: DeprecationContext) => { if (this.deprecations[deprecation.pluginId]) { throw new Error(`Plugin "${deprecation.pluginId}" is duplicated.`); } @@ -29,11 +19,11 @@ export class Deprecations { this.deprecations[deprecation.pluginId] = deprecation; }; - public getDeprecationsByPluginId = (pluginId: string) => { + public getDeprecationInfoByPluginId = (pluginId: string) => { return this.deprecations[pluginId]; }; - public getDeprecations = () => { + public getDeprecationInfo = () => { return this.deprecations; }; } diff --git a/src/plugins/deprecations/server/index.ts b/src/plugins/deprecations/server/index.ts index e90ec8c9a3a3b..c87ea670f4930 100644 --- a/src/plugins/deprecations/server/index.ts +++ b/src/plugins/deprecations/server/index.ts @@ -9,5 +9,7 @@ import { PluginInitializerContext } from 'src/core/server'; import { DeprecationsPlugin } from './plugin'; +export { DeprecationDependencies, DeprecationInfo, DeprecationContext } from './types'; + export const plugin = (initializerContext: PluginInitializerContext) => new DeprecationsPlugin(initializerContext); diff --git a/src/plugins/deprecations/server/routes/deprecations.ts b/src/plugins/deprecations/server/routes/deprecations.ts index b9f1ae984616f..cc19565d2c6c2 100644 --- a/src/plugins/deprecations/server/routes/deprecations.ts +++ b/src/plugins/deprecations/server/routes/deprecations.ts @@ -7,6 +7,7 @@ */ import { IRouter } from 'src/core/server'; +import { DeprecationDependencies, DeprecationInfo, DeprecationContext } from '../types'; export function registerDeprecationRoutes(router: IRouter, deprecations: any) { router.get( @@ -15,24 +16,38 @@ export function registerDeprecationRoutes(router: IRouter, deprecations: any) { validate: false, }, async (context, request, response) => { - const deps = deprecations.getDeprecations(); + const deprecationInfo: DeprecationContext = deprecations.getDeprecationInfo(); - const esClient = context.core.elasticsearch.client; + const dependencies: DeprecationDependencies = { + esClient: context.core.elasticsearch.client, + savedObjectsClient: context.core.savedObjects.client, + }; - const allDeps = await Promise.all( - Object.entries(deps).map(async ([key, value]) => { - const pluginDeprecations = await value.getDeprecations(esClient); - const pluginDeprecationsWithId = pluginDeprecations.map((deps) => ({ - ...deps, - pluginId: key, + const pluginDeprecationsList = await Promise.all( + Object.entries(deprecationInfo).map(async ([pluginId, deprecationInfoContext]) => { + const pluginDeprecations: DeprecationInfo[] = await deprecationInfoContext.getDeprecations( + dependencies + ); + const pluginDeprecationsWithId = pluginDeprecations.map((pluginDeprecation) => ({ + ...pluginDeprecation, + pluginId, })); return pluginDeprecationsWithId; }) - ); + ).catch((error) => { + // TODO handle error + // eslint-disable-next-line no-console + console.log('error', error); + }); + + const flattenedPluginDeprecationsList = + pluginDeprecationsList && pluginDeprecationsList.length + ? pluginDeprecationsList.flat() + : []; return response.ok({ body: { - deprecations: allDeps.flat(), + deprecations: flattenedPluginDeprecationsList, }, }); } diff --git a/src/plugins/deprecations/server/routes/index.ts b/src/plugins/deprecations/server/routes/index.ts index 594a75379781b..2c1ef6eedf09d 100644 --- a/src/plugins/deprecations/server/routes/index.ts +++ b/src/plugins/deprecations/server/routes/index.ts @@ -9,6 +9,8 @@ import { IRouter } from 'src/core/server'; import { registerDeprecationRoutes } from './deprecations'; +export { DeprecationDependencies } from './deprecations'; + export function setupRoutes({ router, deprecations }: { router: IRouter; deprecations: any }) { registerDeprecationRoutes(router, deprecations); } diff --git a/src/plugins/deprecations/server/types.ts b/src/plugins/deprecations/server/types.ts index 59c40480e93e1..4cf0c4b7d1451 100644 --- a/src/plugins/deprecations/server/types.ts +++ b/src/plugins/deprecations/server/types.ts @@ -1,4 +1,31 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { IScopedClusterClient, SavedObjectsClientContract } from 'src/core/server'; + // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface DeprecationsPluginSetup {} + // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface DeprecationsPluginStart {} + +export interface DeprecationDependencies { + esClient: IScopedClusterClient; + savedObjectsClient: SavedObjectsClientContract; +} + +export interface DeprecationInfo { + message: string; + level: 'warning' | 'critical'; + documentationUrl?: string; + correctionAction?: () => void; +} +export interface DeprecationContext { + pluginId: string; + getDeprecations: Promise; +} diff --git a/src/plugins/deprecations/tsconfig.json b/src/plugins/deprecations/tsconfig.json index b705b5255461f..01e00000e9724 100644 --- a/src/plugins/deprecations/tsconfig.json +++ b/src/plugins/deprecations/tsconfig.json @@ -13,5 +13,9 @@ "common/*", "../../../typings/**/*" ], - "references": [] + "references": [ + { + "path": "../../core/tsconfig.json" + }, + ] } diff --git a/src/plugins/timelion/server/deprecations.ts b/src/plugins/timelion/server/deprecations.ts index bc7fc3b4e9256..d4d78edea15bf 100644 --- a/src/plugins/timelion/server/deprecations.ts +++ b/src/plugins/timelion/server/deprecations.ts @@ -6,37 +6,29 @@ * Side Public License, v 1. */ -import { SavedObjectsServiceStart } from 'src/core/server'; +import { DeprecationDependencies } from 'src/plugins/deprecations/server'; -export const getDeprecations = async (savedObjects: SavedObjectsServiceStart) => { +export const getDeprecations = async ({ savedObjectsClient }: DeprecationDependencies) => { const deprecations = []; - // const savedObjectsClient = savedObjects.createInternalRepository(); - // const getTimelionSheets = () => - // savedObjectsClient - // .find({ - // type: 'timelion-sheet', - // perPage: 1, - // }) - // .then(({ total }) => total); + const getTimelionSheets = () => + savedObjectsClient + .find({ + type: 'timelion-sheet', + perPage: 1, + }) + .then(({ total }: { total: number }) => total); - // const timelionWorksheets = await getTimelionSheets(); + const timelionWorksheets = await getTimelionSheets(); - // if (timelionWorksheets > 0) { - // deprecations.push({ - // message: `You have ${timelionWorksheets} Timelion worksheets. The Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard.`, - // documentationUrl: - // 'https://www.elastic.co/guide/en/kibana/master/dashboard.html#timelion-deprecation', - // level: 'warning', - // }); - // } - - deprecations.push({ - message: `You have 1 Timelion worksheets. The Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard.`, - documentationUrl: - 'https://www.elastic.co/guide/en/kibana/master/dashboard.html#timelion-deprecation', - level: 'warning', - }); + if (timelionWorksheets > 0) { + deprecations.push({ + message: `You have ${timelionWorksheets} Timelion worksheets. The Timelion app will be removed in 8.0. To continue using your Timelion worksheets, migrate them to a dashboard.`, + documentationUrl: + 'https://www.elastic.co/guide/en/kibana/master/dashboard.html#timelion-deprecation', + level: 'warning', + }); + } return deprecations; }; diff --git a/src/plugins/timelion/server/plugin.ts b/src/plugins/timelion/server/plugin.ts index 6e13f48583ba4..b010a7132c32d 100644 --- a/src/plugins/timelion/server/plugin.ts +++ b/src/plugins/timelion/server/plugin.ts @@ -13,6 +13,10 @@ import { TimelionConfigType } from './config'; import { timelionSheetSavedObjectType } from './saved_objects'; import { getDeprecations } from './deprecations'; +export interface PluginSetupDependencies { + deprecations: any; // TODO fix +} + /** * Deprecated since 7.0, the Timelion app will be removed in 8.0. * To continue using your Timelion worksheets, migrate them to a dashboard. @@ -20,7 +24,9 @@ import { getDeprecations } from './deprecations'; * @link https://www.elastic.co/guide/en/kibana/master/timelion.html#timelion-deprecation **/ const showWarningMessageIfTimelionSheetWasFound = async (core: CoreStart, logger: Logger) => { - const deprecations = await getDeprecations(core.savedObjects); + const savedObjectsClient = core.savedObjects.createInternalRepository(); + // TODO fix + const deprecations = await getDeprecations({ savedObjectsClient }); if (deprecations.length) { logger.warn( @@ -36,7 +42,7 @@ export class TimelionPlugin implements Plugin { this.logger = context.logger.get(); } - public setup(core: CoreSetup, { deprecations }) { + public setup(core: CoreSetup, { deprecations }: PluginSetupDependencies) { core.capabilities.registerProvider(() => ({ timelion: { save: true, diff --git a/src/plugins/timelion/tsconfig.json b/src/plugins/timelion/tsconfig.json index 5b96d69a878ea..b79341fe70be9 100644 --- a/src/plugins/timelion/tsconfig.json +++ b/src/plugins/timelion/tsconfig.json @@ -19,5 +19,8 @@ { "path": "../vis_type_timelion/tsconfig.json" }, { "path": "../saved_objects/tsconfig.json" }, { "path": "../kibana_legacy/tsconfig.json" }, + { + "path": "../deprecations/tsconfig.json" + }, ] } diff --git a/x-pack/plugins/security/server/deprecations/deprecations.ts b/x-pack/plugins/security/server/deprecations/deprecations.ts index b083575568bca..8c65a9c6ac815 100644 --- a/x-pack/plugins/security/server/deprecations/deprecations.ts +++ b/x-pack/plugins/security/server/deprecations/deprecations.ts @@ -5,11 +5,7 @@ * 2.0. */ -import { ElasticsearchClient, IClusterClient, IScopedClusterClient } from 'kibana/server'; - -export interface DeprecationsServiceSetup { - getClusterClient: () => Promise; -} +import { DeprecationDependencies } from 'src/plugins/deprecations/server'; interface UserInfo { username: string; @@ -22,70 +18,62 @@ interface UserInfo { enabled: boolean; } -export class DeprecationsService { - private elasticsearchClient?: IClusterClient; +const isUsingDeprecatedRole = (users: Record, role: string) => { + const usersWithDeprecatedRoles = Object.keys(users).filter((user) => { + return users[user].roles.includes(role); + }); - private isUsingDeprecatedRole(users: Record, role: string) { - const usersWithDeprecatedRoles = Object.keys(users).filter((user) => { - return users[user].roles.includes(role); - }); + return usersWithDeprecatedRoles.map((user) => { + const userInfo = users[user]; + return { + message: `User '${userInfo.username}' is using a deprecated role: '${role}'`, + correctiveAction: '', + documentationUrl: '', + level: 'critical', + }; + }); +}; - return usersWithDeprecatedRoles.map((user) => { - const userInfo = users[user]; - return { - message: `User '${userInfo.username}' is using a deprecated role: '${role}'`, - correctiveAction: '', - documentationUrl: '', - level: 'critical', - }; - }); - } +const isUsingDeprecatedUser = (users: Record, username: string) => { + const deprecatedUsers = Object.keys(users).filter((user) => { + return users[user].username === username; + }); - private isUsingDeprecatedUser(users: Record, username: string) { - const deprecatedUsers = Object.keys(users).filter((user) => { - return users[user].username === username; - }); + return deprecatedUsers.map((user) => { + const userInfo = users[user]; + return { + message: `User '${userInfo.username}' has been deprecated.`, + correctiveAction: '', + documentationUrl: '', + level: 'critical', + }; + }); +}; - return deprecatedUsers.map((user) => { - const userInfo = users[user]; - return { - message: `User '${userInfo.username}' has been deprecated.`, - correctiveAction: '', - documentationUrl: '', - level: 'critical', - }; - }); - } +export const getDeprecations = async ({ esClient }: DeprecationDependencies) => { + try { + const { body: usersResponse } = await esClient.asCurrentUser.security.getUser< + Record + >(); - public async setup() {} + const usersWithDeprecatedKibanaUserRole = isUsingDeprecatedRole(usersResponse, 'kibana_user'); + const usersWithDeprecatedDashboardRole = isUsingDeprecatedRole( + usersResponse, + 'kibana_dashboard_only_user' + ); - public async getDeprecations(esClient: IScopedClusterClient) { - try { - const { body: usersResponse } = await esClient.asCurrentUser.security.getUser< - Record - >(); + const deprecatedUsers = isUsingDeprecatedUser(usersResponse, 'kibana'); - const usersWithDeprecatedKibanaUserRole = this.isUsingDeprecatedRole( - usersResponse, - 'kibana_user' - ); - const usersWithDeprecatedDashboardRole = this.isUsingDeprecatedRole( - usersResponse, - 'kibana_dashboard_only_user' - ); + const deprecations = [ + ...usersWithDeprecatedKibanaUserRole, + ...usersWithDeprecatedDashboardRole, + ...deprecatedUsers, + ]; - const deprecatedUsers = this.isUsingDeprecatedUser(usersResponse, 'kibana'); - - const deprecations = [ - ...usersWithDeprecatedKibanaUserRole, - ...usersWithDeprecatedDashboardRole, - ...deprecatedUsers, - ]; - - return deprecations; - } catch (error) { - // TODO handle error - console.log('error', error); - } + return deprecations; + } catch (error) { + // TODO handle error + // eslint-disable-next-line no-console + console.log('error', error); } -} +}; diff --git a/x-pack/plugins/security/server/deprecations/index.ts b/x-pack/plugins/security/server/deprecations/index.ts index 5aeab91127d1a..9246604dc96a4 100644 --- a/x-pack/plugins/security/server/deprecations/index.ts +++ b/x-pack/plugins/security/server/deprecations/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { DeprecationsService } from './deprecations'; +export { getDeprecations } from './deprecations'; diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index e7354f3fabb2b..be84f62969cd2 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -43,7 +43,7 @@ import { ElasticsearchService } from './elasticsearch'; import { Session, SessionManagementService } from './session_management'; import { registerSecurityUsageCollector } from './usage_collector'; import { setupSpacesClient } from './spaces'; -import { DeprecationsService } from './deprecations'; +import { getDeprecations } from './deprecations'; export type SpacesService = Pick< SpacesPluginSetup['spacesService'], @@ -89,7 +89,7 @@ export interface PluginSetupDependencies { features: FeaturesPluginSetup; licensing: LicensingPluginSetup; taskManager: TaskManagerSetupContract; - deprecations: any; + deprecations: any; // TODO fix usageCollection?: UsageCollectionSetup; securityOss?: SecurityOssPluginSetup; spaces?: SpacesPluginSetup; @@ -175,7 +175,6 @@ export class SecurityPlugin this.initializerContext.logger.get('anonymous-access'), this.getConfig ); - private readonly deprecationsService = new DeprecationsService(); constructor(private readonly initializerContext: PluginInitializerContext) { this.logger = this.initializerContext.logger.get(); @@ -304,7 +303,7 @@ export class SecurityPlugin deprecations.registerDeprecations({ pluginId: 'security', - getDeprecations: this.deprecationsService.getDeprecations.bind(this.deprecationsService), + getDeprecations, }); return Object.freeze({ diff --git a/x-pack/plugins/security/server/routes/deprecations/get.ts b/x-pack/plugins/security/server/routes/deprecations/get.ts deleted file mode 100644 index 9e6373af8ef86..0000000000000 --- a/x-pack/plugins/security/server/routes/deprecations/get.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { createLicensedRouteHandler } from '../licensed_route_handler'; -import { wrapError } from '../../errors'; -import { RouteDefinitionParams } from '..'; - -interface UserInfo { - username: string; - roles: string[]; - full_name: string | null; - email: string | null; - metadata: { - [key: string]: any; - }; - enabled: boolean; -} - -const isUsingDeprecatedRole = (users: Record, role: string) => { - const usersWithDeprecatedRoles = Object.keys(users).filter((user) => { - return users[user].roles.includes(role); - }); - - return usersWithDeprecatedRoles.map((user) => { - const userInfo = users[user]; - return { - message: `User '${userInfo.username}' is using a deprecated role: '${role}'`, - correctiveAction: '', - documentationUrl: '', - level: 'critical', - }; - }); -}; - -const isUsingDeprecatedUser = (users: Record, username: string) => { - const deprecatedUsers = Object.keys(users).filter((user) => { - return users[user].username === username; - }); - - return deprecatedUsers.map((user) => { - const userInfo = users[user]; - return { - message: `User '${userInfo.username}' has been deprecated.`, - correctiveAction: '', - documentationUrl: '', - level: 'critical', - }; - }); -}; - -export function defineDeprecationsGetRoutes(params: RouteDefinitionParams) { - const { logger, router } = params; - - router.get( - { - path: '/internal/security/deprecations', - validate: false, - }, - createLicensedRouteHandler(async (context, request, response) => { - try { - const { - body: usersResponse, - } = await context.core.elasticsearch.client.asCurrentUser.security.getUser< - Record - >(); - - const usersWithDeprecatedRoles = [ - ...isUsingDeprecatedRole(usersResponse, 'kibana_user'), - ...isUsingDeprecatedRole(usersResponse, 'kibana_dashboard_only_user'), - ]; - const deprecatedUsers = isUsingDeprecatedUser(usersResponse, 'kibana'); - - const deprecations = [...usersWithDeprecatedRoles, ...deprecatedUsers]; - - return response.ok({ body: deprecations }); - } catch (error) { - const wrappedError = wrapError(error); - return response.customError({ - body: wrappedError, - statusCode: wrappedError.output.statusCode, - }); - } - }) - ); -} diff --git a/x-pack/plugins/security/server/routes/deprecations/index.ts b/x-pack/plugins/security/server/routes/deprecations/index.ts deleted file mode 100644 index 0a7a7337defc2..0000000000000 --- a/x-pack/plugins/security/server/routes/deprecations/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { defineDeprecationsGetRoutes } from './get'; -import { RouteDefinitionParams } from '..'; - -export function defineDeprecationRoutes(params: RouteDefinitionParams) { - defineDeprecationsGetRoutes(params); -} diff --git a/x-pack/plugins/security/server/routes/index.ts b/x-pack/plugins/security/server/routes/index.ts index 073d17b83438e..ba59d0f39692b 100644 --- a/x-pack/plugins/security/server/routes/index.ts +++ b/x-pack/plugins/security/server/routes/index.ts @@ -24,7 +24,6 @@ import { defineUsersRoutes } from './users'; import { defineRoleMappingRoutes } from './role_mapping'; import { defineSessionManagementRoutes } from './session_management'; import { defineViewRoutes } from './views'; -import { defineDeprecationRoutes } from './deprecations'; /** * Describes parameters used to define HTTP routes. @@ -52,5 +51,4 @@ export function defineRoutes(params: RouteDefinitionParams) { defineUsersRoutes(params); defineRoleMappingRoutes(params); defineViewRoutes(params); - defineDeprecationRoutes(params); } diff --git a/x-pack/plugins/security/tsconfig.json b/x-pack/plugins/security/tsconfig.json index 6c3fd1851a8cb..a85defc2a2d31 100644 --- a/x-pack/plugins/security/tsconfig.json +++ b/x-pack/plugins/security/tsconfig.json @@ -19,6 +19,9 @@ { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/management/tsconfig.json" }, { "path": "../../../src/plugins/security_oss/tsconfig.json" }, - { "path": "../../../src/plugins/usage_collection/tsconfig.json" } + { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, + { + "path": "../../../src/plugins/deprecations/tsconfig.json" + } ] } From dc51be5295d7da981d5d29f42476492221f2fb75 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Wed, 17 Feb 2021 12:21:30 -0500 Subject: [PATCH 3/3] add support for embeddable action --- src/plugins/deprecations/server/types.ts | 12 ++- src/plugins/timelion/server/deprecations.ts | 10 ++ .../server/deprecations/deprecations.ts | 91 ++++++++++++++++--- 3 files changed, 96 insertions(+), 17 deletions(-) diff --git a/src/plugins/deprecations/server/types.ts b/src/plugins/deprecations/server/types.ts index 4cf0c4b7d1451..c333becc57790 100644 --- a/src/plugins/deprecations/server/types.ts +++ b/src/plugins/deprecations/server/types.ts @@ -18,12 +18,20 @@ export interface DeprecationDependencies { esClient: IScopedClusterClient; savedObjectsClient: SavedObjectsClientContract; } - export interface DeprecationInfo { message: string; level: 'warning' | 'critical'; documentationUrl?: string; - correctionAction?: () => void; + correctionActions: { + api?: { + path: string; + method: 'POST' | 'PUT'; + body?: { + [key: string]: any; + }; + }; + manualSteps?: string[]; + }; } export interface DeprecationContext { pluginId: string; diff --git a/src/plugins/timelion/server/deprecations.ts b/src/plugins/timelion/server/deprecations.ts index d4d78edea15bf..2154c4d19fc17 100644 --- a/src/plugins/timelion/server/deprecations.ts +++ b/src/plugins/timelion/server/deprecations.ts @@ -27,6 +27,16 @@ export const getDeprecations = async ({ savedObjectsClient }: DeprecationDepende documentationUrl: 'https://www.elastic.co/guide/en/kibana/master/dashboard.html#timelion-deprecation', level: 'warning', + correctiveActions: { + manualSteps: [ + 'Navigate to the Kibana Dashboard and click "Create dashboard".', + 'Select Timelion from the "New Visualization" window.', + 'Open a new tab, open the Timelion app, select the chart you want to copy, then copy the chart expression.', + 'Go to Timelion, paste the chart expression in the Timelion expression field, then click Update.', + 'In the toolbar, click Save.', + 'On the Save visualization window, enter the visualization Title, then click Save and return.', + ], + }, }); } diff --git a/x-pack/plugins/security/server/deprecations/deprecations.ts b/x-pack/plugins/security/server/deprecations/deprecations.ts index 8c65a9c6ac815..2887bfc7abf13 100644 --- a/x-pack/plugins/security/server/deprecations/deprecations.ts +++ b/x-pack/plugins/security/server/deprecations/deprecations.ts @@ -18,33 +18,71 @@ interface UserInfo { enabled: boolean; } -const isUsingDeprecatedRole = (users: Record, role: string) => { +const getRoleDeprecations = ({ + users, + deprecatedRole, + manualSteps, + apiInfo, +}: { + users: Record; + deprecatedRole: string; + manualSteps?: string[]; + apiInfo?: { + newRole: string; + }; +}) => { const usersWithDeprecatedRoles = Object.keys(users).filter((user) => { - return users[user].roles.includes(role); + return users[user].roles.includes(deprecatedRole); }); return usersWithDeprecatedRoles.map((user) => { const userInfo = users[user]; + const filteredRoles = userInfo.roles.filter((userInfoRole) => userInfoRole !== deprecatedRole); + return { - message: `User '${userInfo.username}' is using a deprecated role: '${role}'`, - correctiveAction: '', - documentationUrl: '', + message: `User '${userInfo.username}' is using a deprecated role: '${deprecatedRole}'`, + correctiveActions: { + api: apiInfo + ? { + path: `/internal/security/users/${userInfo.username}`, + method: 'POST', + body: { + ...userInfo, + roles: [...filteredRoles, apiInfo.newRole], + }, + } + : undefined, + manualSteps, + }, + documentationUrl: + 'https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-put-user.html', level: 'critical', }; }); }; -const isUsingDeprecatedUser = (users: Record, username: string) => { +const getUserDeprecations = ({ + users, + deprecatedUser, + manualSteps, +}: { + users: Record; + deprecatedUser: string; + manualSteps: string[]; +}) => { const deprecatedUsers = Object.keys(users).filter((user) => { - return users[user].username === username; + return users[user].username === deprecatedUser; }); return deprecatedUsers.map((user) => { const userInfo = users[user]; return { message: `User '${userInfo.username}' has been deprecated.`, - correctiveAction: '', - documentationUrl: '', + correctiveActions: { + manualSteps, + }, + documentationUrl: + 'https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-put-user.html', level: 'critical', }; }); @@ -56,13 +94,36 @@ export const getDeprecations = async ({ esClient }: DeprecationDependencies) => Record >(); - const usersWithDeprecatedKibanaUserRole = isUsingDeprecatedRole(usersResponse, 'kibana_user'); - const usersWithDeprecatedDashboardRole = isUsingDeprecatedRole( - usersResponse, - 'kibana_dashboard_only_user' - ); + const usersWithDeprecatedKibanaUserRole = getRoleDeprecations({ + users: usersResponse, + deprecatedRole: 'kibana_user', + apiInfo: { + newRole: 'kibana_admin', + }, + manualSteps: [ + 'Using Kibana user management, change all users using the kibana_user role to the kibana_admin role.', + 'Using Kibana role-mapping management, change all role-mappings which assing the kibana_user role to the kibana_admin role.', + ], + }); + + const usersWithDeprecatedDashboardRole = getRoleDeprecations({ + users: usersResponse, + deprecatedRole: 'kibana_dashboard_only_user', + manualSteps: [ + 'Using Kibana role management, create a new custom role.', + 'Assign read-only access to the Dashboard feature.', + 'Assign this role in place of the dashboard_only role.', + ], + }); - const deprecatedUsers = isUsingDeprecatedUser(usersResponse, 'kibana'); + const deprecatedUsers = getUserDeprecations({ + users: usersResponse, + deprecatedUser: 'kibana', + manualSteps: [ + 'Using Kibana user management, set the password for the kibana_system user', + 'Update all kibana.yml files to use this username and password for elasticsearch.username and elasticsearch.password', + ], + }); const deprecations = [ ...usersWithDeprecatedKibanaUserRole,