From 35214416f80901a47e41de7e694a4894b863aeac Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Thu, 15 Apr 2021 14:54:49 -0700 Subject: [PATCH] Extract License service from CCR and Watcher into license_api_guard plugin in x-pack (#95973) * Localize error messages. --- .github/CODEOWNERS | 1 + docs/developer/plugin-list.asciidoc | 4 + src/plugins/es_ui_shared/tsconfig.json | 2 +- tsconfig.json | 1 + x-pack/.i18nrc.json | 1 + .../cross_cluster_replication/kibana.json | 1 + .../server/plugin.ts | 37 +++--- .../register_create_route.test.ts | 3 +- .../register_delete_route.test.ts | 3 +- .../register_fetch_route.test.ts | 3 +- .../register_get_route.test.ts | 3 +- .../register_pause_route.test.ts | 3 +- .../register_resume_route.test.ts | 3 +- .../register_update_route.test.ts | 3 +- .../register_create_route.test.ts | 3 +- .../register_fetch_route.test.ts | 3 +- .../follower_index/register_get_route.test.ts | 3 +- .../register_pause_route.test.ts | 3 +- .../register_resume_route.test.ts | 3 +- .../register_unfollow_route.test.ts | 3 +- .../server/services/index.ts | 1 - .../server/services/license.ts | 91 -------------- .../server/shared_imports.ts | 1 + .../cross_cluster_replication/server/types.ts | 11 +- .../cross_cluster_replication/tsconfig.json | 1 + x-pack/plugins/license_api_guard/READM.md | 3 + .../jest.config.js} | 6 +- x-pack/plugins/license_api_guard/kibana.json | 8 ++ .../plugins/license_api_guard/server/index.ts | 16 +++ .../license_api_guard/server/license.test.ts | 105 ++++++++++++++++ .../license_api_guard/server/license.ts | 113 ++++++++++++++++++ .../server/shared_imports.ts | 12 ++ .../plugins/license_api_guard/tsconfig.json | 17 +++ .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - x-pack/plugins/watcher/kibana.json | 1 + .../license_pre_routing_factory.test.js | 40 ------- .../license_pre_routing_factory.ts | 32 ----- x-pack/plugins/watcher/server/plugin.ts | 64 +++++----- .../routes/api/indices/register_get_route.ts | 8 +- .../api/license/register_refresh_route.ts | 7 +- .../routes/api/register_list_fields_route.ts | 12 +- .../routes/api/register_load_history_route.ts | 12 +- .../api/settings/register_load_route.ts | 8 +- .../action/register_acknowledge_route.ts | 12 +- .../api/watch/register_activate_route.ts | 8 +- .../api/watch/register_deactivate_route.ts | 12 +- .../routes/api/watch/register_delete_route.ts | 8 +- .../api/watch/register_execute_route.ts | 8 +- .../api/watch/register_history_route.ts | 8 +- .../routes/api/watch/register_load_route.ts | 8 +- .../routes/api/watch/register_save_route.ts | 8 +- .../api/watch/register_visualize_route.ts | 8 +- .../api/watches/register_delete_route.ts | 7 +- .../routes/api/watches/register_list_route.ts | 8 +- .../plugins/watcher/server/shared_imports.ts | 1 + x-pack/plugins/watcher/server/types.ts | 19 +-- x-pack/plugins/watcher/tsconfig.json | 1 + 58 files changed, 433 insertions(+), 340 deletions(-) delete mode 100644 x-pack/plugins/cross_cluster_replication/server/services/license.ts create mode 100644 x-pack/plugins/license_api_guard/READM.md rename x-pack/plugins/{watcher/server/lib/license_pre_routing_factory/index.ts => license_api_guard/jest.config.js} (67%) create mode 100644 x-pack/plugins/license_api_guard/kibana.json create mode 100644 x-pack/plugins/license_api_guard/server/index.ts create mode 100644 x-pack/plugins/license_api_guard/server/license.test.ts create mode 100644 x-pack/plugins/license_api_guard/server/license.ts create mode 100644 x-pack/plugins/license_api_guard/server/shared_imports.ts create mode 100644 x-pack/plugins/license_api_guard/tsconfig.json delete mode 100644 x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.test.js delete mode 100644 x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 92e39c2e634e..0692e94e8b02 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -312,6 +312,7 @@ /x-pack/plugins/console_extensions/ @elastic/es-ui /x-pack/plugins/grokdebugger/ @elastic/es-ui /x-pack/plugins/index_management/ @elastic/es-ui +/x-pack/plugins/license_api_guard/ @elastic/es-ui /x-pack/plugins/license_management/ @elastic/es-ui /x-pack/plugins/painless_lab/ @elastic/es-ui /x-pack/plugins/remote_clusters/ @elastic/es-ui diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 353a77527d1d..de7253e34d10 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -444,6 +444,10 @@ the infrastructure monitoring use-case within Kibana. |Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. +|{kib-repo}blob/{branch}/x-pack/plugins/license_api_guard[licenseApiGuard] +|WARNING: Missing README. + + |{kib-repo}blob/{branch}/x-pack/plugins/license_management/README.md[licenseManagement] |This plugin enables users to activate a trial license, downgrade to Basic, and upload a new license. diff --git a/src/plugins/es_ui_shared/tsconfig.json b/src/plugins/es_ui_shared/tsconfig.json index 9bcda2e0614d..5f136d09b2ce 100644 --- a/src/plugins/es_ui_shared/tsconfig.json +++ b/src/plugins/es_ui_shared/tsconfig.json @@ -16,6 +16,6 @@ ], "references": [ { "path": "../../core/tsconfig.json" }, - { "path": "../data/tsconfig.json" }, + { "path": "../data/tsconfig.json" } ] } diff --git a/tsconfig.json b/tsconfig.json index 40763ede1bbd..ac15fe14b4d2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -105,6 +105,7 @@ { "path": "./x-pack/plugins/infra/tsconfig.json" }, { "path": "./x-pack/plugins/ingest_pipelines/tsconfig.json" }, { "path": "./x-pack/plugins/lens/tsconfig.json" }, + { "path": "./x-pack/plugins/license_api_guard/tsconfig.json" }, { "path": "./x-pack/plugins/license_management/tsconfig.json" }, { "path": "./x-pack/plugins/licensing/tsconfig.json" }, { "path": "./x-pack/plugins/logstash/tsconfig.json" }, diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 6bbbf6cd6b82..3fee52ff5585 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -31,6 +31,7 @@ "xpack.fleet": "plugins/fleet", "xpack.ingestPipelines": "plugins/ingest_pipelines", "xpack.lens": "plugins/lens", + "xpack.licenseApiGuard": "plugins/license_api_guard", "xpack.licenseMgmt": "plugins/license_management", "xpack.licensing": "plugins/licensing", "xpack.lists": "plugins/lists", diff --git a/x-pack/plugins/cross_cluster_replication/kibana.json b/x-pack/plugins/cross_cluster_replication/kibana.json index 292820f81adb..f130d0173cc8 100644 --- a/x-pack/plugins/cross_cluster_replication/kibana.json +++ b/x-pack/plugins/cross_cluster_replication/kibana.json @@ -6,6 +6,7 @@ "requiredPlugins": [ "home", "licensing", + "licenseApiGuard", "management", "remoteClusters", "indexManagement", diff --git a/x-pack/plugins/cross_cluster_replication/server/plugin.ts b/x-pack/plugins/cross_cluster_replication/server/plugin.ts index 1150f191441f..e3a1de1dbfab 100644 --- a/x-pack/plugins/cross_cluster_replication/server/plugin.ts +++ b/x-pack/plugins/cross_cluster_replication/server/plugin.ts @@ -7,9 +7,9 @@ import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; -import { i18n } from '@kbn/i18n'; import { CoreSetup, + CoreStart, ILegacyCustomClusterClient, Plugin, Logger, @@ -19,12 +19,11 @@ import { import { Index } from '../../index_management/server'; import { PLUGIN } from '../common/constants'; -import type { Dependencies, CcrRequestHandlerContext } from './types'; +import { SetupDependencies, StartDependencies, CcrRequestHandlerContext } from './types'; import { registerApiRoutes } from './routes'; -import { License } from './services'; import { elasticsearchJsPlugin } from './client/elasticsearch_ccr'; import { CrossClusterReplicationConfig } from './config'; -import { isEsError } from './shared_imports'; +import { License, isEsError } from './shared_imports'; import { formatEsError } from './lib/format_es_error'; async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { @@ -77,7 +76,7 @@ export class CrossClusterReplicationServerPlugin implements Plugin { - const { state, message } = license.check(pluginId, minimumLicenseType); - const hasRequiredLicense = state === 'valid'; - - // Retrieving security checks the results of GET /_xpack as well as license state, - // so we're also checking whether the security is disabled in elasticsearch.yml. - this._isEsSecurityEnabled = license.getFeature('security').isEnabled; - - if (hasRequiredLicense) { - this.licenseStatus = { isValid: true }; - } else { - this.licenseStatus = { - isValid: false, - message: message || defaultErrorMessage, - }; - if (message) { - logger.info(message); - } - } - }); - } - - guardApiRoute(handler: RequestHandler) { - const license = this; - - return function licenseCheck( - ctx: CcrRequestHandlerContext, - request: KibanaRequest, - response: KibanaResponseFactory - ) { - const licenseStatus = license.getStatus(); - - if (!licenseStatus.isValid) { - return response.customError({ - body: { - message: licenseStatus.message || '', - }, - statusCode: 403, - }); - } - - return handler(ctx, request, response); - }; - } - - getStatus() { - return this.licenseStatus; - } - - // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility - get isEsSecurityEnabled() { - return this._isEsSecurityEnabled; - } -} diff --git a/x-pack/plugins/cross_cluster_replication/server/shared_imports.ts b/x-pack/plugins/cross_cluster_replication/server/shared_imports.ts index df9b3dd53cc1..4252a2a5c32d 100644 --- a/x-pack/plugins/cross_cluster_replication/server/shared_imports.ts +++ b/x-pack/plugins/cross_cluster_replication/server/shared_imports.ts @@ -6,3 +6,4 @@ */ export { isEsError } from '../../../../src/plugins/es_ui_shared/server'; +export { License } from '../../license_api_guard/server'; diff --git a/x-pack/plugins/cross_cluster_replication/server/types.ts b/x-pack/plugins/cross_cluster_replication/server/types.ts index 2bec53170084..7314fda70027 100644 --- a/x-pack/plugins/cross_cluster_replication/server/types.ts +++ b/x-pack/plugins/cross_cluster_replication/server/types.ts @@ -7,20 +7,23 @@ import { IRouter, ILegacyScopedClusterClient, RequestHandlerContext } from 'src/core/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; -import { LicensingPluginSetup } from '../../licensing/server'; +import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/server'; import { IndexManagementPluginSetup } from '../../index_management/server'; import { RemoteClustersPluginSetup } from '../../remote_clusters/server'; -import { License } from './services'; -import { isEsError } from './shared_imports'; +import { License, isEsError } from './shared_imports'; import { formatEsError } from './lib/format_es_error'; -export interface Dependencies { +export interface SetupDependencies { licensing: LicensingPluginSetup; indexManagement: IndexManagementPluginSetup; remoteClusters: RemoteClustersPluginSetup; features: FeaturesPluginSetup; } +export interface StartDependencies { + licensing: LicensingPluginStart; +} + export interface RouteDependencies { router: CcrPluginRouter; license: License; diff --git a/x-pack/plugins/cross_cluster_replication/tsconfig.json b/x-pack/plugins/cross_cluster_replication/tsconfig.json index 9c7590b9c255..e0923553bead 100644 --- a/x-pack/plugins/cross_cluster_replication/tsconfig.json +++ b/x-pack/plugins/cross_cluster_replication/tsconfig.json @@ -27,5 +27,6 @@ { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/es_ui_shared/tsconfig.json" }, { "path": "../../../src/plugins/data/tsconfig.json" }, + { "path": "../license_api_guard/tsconfig.json" }, ] } diff --git a/x-pack/plugins/license_api_guard/READM.md b/x-pack/plugins/license_api_guard/READM.md new file mode 100644 index 000000000000..767223125b12 --- /dev/null +++ b/x-pack/plugins/license_api_guard/READM.md @@ -0,0 +1,3 @@ +# License API guard plugin + +This plugin is used by ES UI plugins to reject API requests to plugins that are unsupported by the user's license. \ No newline at end of file diff --git a/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/index.ts b/x-pack/plugins/license_api_guard/jest.config.js similarity index 67% rename from x-pack/plugins/watcher/server/lib/license_pre_routing_factory/index.ts rename to x-pack/plugins/license_api_guard/jest.config.js index a86cdb1f20f7..e0f348ceabd8 100644 --- a/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/index.ts +++ b/x-pack/plugins/license_api_guard/jest.config.js @@ -5,4 +5,8 @@ * 2.0. */ -export { licensePreRoutingFactory } from './license_pre_routing_factory'; +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/x-pack/plugins/license_api_guard'], +}; diff --git a/x-pack/plugins/license_api_guard/kibana.json b/x-pack/plugins/license_api_guard/kibana.json new file mode 100644 index 000000000000..0fdf7ffed898 --- /dev/null +++ b/x-pack/plugins/license_api_guard/kibana.json @@ -0,0 +1,8 @@ +{ + "id": "licenseApiGuard", + "version": "0.0.1", + "kibanaVersion": "kibana", + "configPath": ["xpack", "licenseApiGuard"], + "server": true, + "ui": false +} diff --git a/x-pack/plugins/license_api_guard/server/index.ts b/x-pack/plugins/license_api_guard/server/index.ts new file mode 100644 index 000000000000..3c4abd4e17c3 --- /dev/null +++ b/x-pack/plugins/license_api_guard/server/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { License } from './license'; + +/** dummy plugin*/ +export function plugin() { + return new (class LicenseApiGuardPlugin { + setup() {} + start() {} + })(); +} diff --git a/x-pack/plugins/license_api_guard/server/license.test.ts b/x-pack/plugins/license_api_guard/server/license.test.ts new file mode 100644 index 000000000000..e9da393f5347 --- /dev/null +++ b/x-pack/plugins/license_api_guard/server/license.test.ts @@ -0,0 +1,105 @@ +/* + * 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 { of } from 'rxjs'; +import type { KibanaRequest, RequestHandlerContext } from 'src/core/server'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { httpServerMock } from 'src/core/server/http/http_server.mocks'; + +import { License } from './license'; +import { LicenseCheckState, licensingMock } from './shared_imports'; + +describe('License API guard', () => { + const pluginName = 'testPlugin'; + const currentLicenseType = 'basic'; + + const testRoute = ({ licenseState }: { licenseState: string }) => { + const license = new License(); + + const logger = { + warn: jest.fn(), + }; + + license.setup({ pluginName, logger }); + + const licenseMock = licensingMock.createLicenseMock(); + licenseMock.type = currentLicenseType; + licenseMock.check('test', 'basic'); // Flush default mocked state + licenseMock.check.mockReturnValue({ state: licenseState as LicenseCheckState }); // Replace with new mocked state + + const licensing = { + license$: of(licenseMock), + }; + + license.start({ + pluginId: 'id', + minimumLicenseType: 'basic', + licensing, + }); + + const route = jest.fn(); + const guardedRoute = license.guardApiRoute(route); + const forbidden = jest.fn(); + const responseMock = httpServerMock.createResponseFactory(); + responseMock.forbidden = forbidden; + guardedRoute({} as RequestHandlerContext, {} as KibanaRequest, responseMock); + + return { + errorResponse: + forbidden.mock.calls.length > 0 + ? forbidden.mock.calls[forbidden.mock.calls.length - 1][0] + : undefined, + logMesssage: + logger.warn.mock.calls.length > 0 + ? logger.warn.mock.calls[logger.warn.mock.calls.length - 1][0] + : undefined, + route, + }; + }; + + describe('valid license', () => { + it('the original route is called and nothing is logged', () => { + const { errorResponse, logMesssage, route } = testRoute({ licenseState: 'valid' }); + + expect(errorResponse).toBeUndefined(); + expect(logMesssage).toBeUndefined(); + expect(route).toHaveBeenCalled(); + }); + }); + + [ + { + licenseState: 'invalid', + expectedMessage: `Your ${currentLicenseType} license does not support ${pluginName}. Please upgrade your license.`, + }, + { + licenseState: 'expired', + expectedMessage: `You cannot use ${pluginName} because your ${currentLicenseType} license has expired.`, + }, + { + licenseState: 'unavailable', + expectedMessage: `You cannot use ${pluginName} because license information is not available at this time.`, + }, + ].forEach(({ licenseState, expectedMessage }) => { + describe(`${licenseState} license`, () => { + it('replies with and logs the error message', () => { + const { errorResponse, logMesssage, route } = testRoute({ licenseState }); + + // We depend on the call to `response.forbidden()` to generate the 403 status code, + // so we can't assert for it here. + expect(errorResponse).toEqual({ + body: { + message: expectedMessage, + }, + }); + + expect(logMesssage).toBe(expectedMessage); + expect(route).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/x-pack/plugins/license_api_guard/server/license.ts b/x-pack/plugins/license_api_guard/server/license.ts new file mode 100644 index 000000000000..3b0fbc8422d6 --- /dev/null +++ b/x-pack/plugins/license_api_guard/server/license.ts @@ -0,0 +1,113 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { + Logger, + KibanaRequest, + KibanaResponseFactory, + RequestHandler, + RequestHandlerContext, +} from 'src/core/server'; + +import { ILicense, LicenseType, LicenseCheckState, LicensingPluginStart } from './shared_imports'; + +type LicenseLogger = Pick; +type LicenseDependency = Pick; + +interface SetupSettings { + pluginName: string; + logger: LicenseLogger; +} + +interface StartSettings { + pluginId: string; + minimumLicenseType: LicenseType; + licensing: LicenseDependency; +} + +export class License { + private pluginName?: string; + private logger?: LicenseLogger; + private licenseCheckState: LicenseCheckState = 'unavailable'; + private licenseType?: LicenseType; + + private _isEsSecurityEnabled: boolean = false; + + setup({ pluginName, logger }: SetupSettings) { + this.pluginName = pluginName; + this.logger = logger; + } + + start({ pluginId, minimumLicenseType, licensing }: StartSettings) { + licensing.license$.subscribe((license: ILicense) => { + this.licenseType = license.type; + this.licenseCheckState = license.check(pluginId, minimumLicenseType!).state; + // Retrieving security checks the results of GET /_xpack as well as license state, + // so we're also checking whether security is disabled in elasticsearch.yml. + this._isEsSecurityEnabled = license.getFeature('security').isEnabled; + }); + } + + private getLicenseErrorMessage(licenseCheckState: LicenseCheckState): string { + switch (licenseCheckState) { + case 'invalid': + return i18n.translate('xpack.licenseApiGuard.license.errorUnsupportedMessage', { + defaultMessage: + 'Your {licenseType} license does not support {pluginName}. Please upgrade your license.', + values: { licenseType: this.licenseType!, pluginName: this.pluginName }, + }); + + case 'expired': + return i18n.translate('xpack.licenseApiGuard.license.errorExpiredMessage', { + defaultMessage: + 'You cannot use {pluginName} because your {licenseType} license has expired.', + values: { licenseType: this.licenseType!, pluginName: this.pluginName }, + }); + + case 'unavailable': + return i18n.translate('xpack.licenseApiGuard.license.errorUnavailableMessage', { + defaultMessage: + 'You cannot use {pluginName} because license information is not available at this time.', + values: { pluginName: this.pluginName }, + }); + } + + return i18n.translate('xpack.licenseApiGuard.license.genericErrorMessage', { + defaultMessage: 'You cannot use {pluginName} because the license check failed.', + values: { pluginName: this.pluginName }, + }); + } + + guardApiRoute( + handler: RequestHandler + ) { + return ( + ctx: Context, + request: KibanaRequest, + response: KibanaResponseFactory + ) => { + // We'll only surface license errors if users attempt disallowed access to the API. + if (this.licenseCheckState !== 'valid') { + const licenseErrorMessage = this.getLicenseErrorMessage(this.licenseCheckState); + this.logger?.warn(licenseErrorMessage); + + return response.forbidden({ + body: { + message: licenseErrorMessage, + }, + }); + } + + return handler(ctx, request, response); + }; + } + + public get isEsSecurityEnabled() { + return this._isEsSecurityEnabled; + } +} diff --git a/x-pack/plugins/license_api_guard/server/shared_imports.ts b/x-pack/plugins/license_api_guard/server/shared_imports.ts new file mode 100644 index 000000000000..1318706df11c --- /dev/null +++ b/x-pack/plugins/license_api_guard/server/shared_imports.ts @@ -0,0 +1,12 @@ +/* + * 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 type { ILicense, LicenseType, LicenseCheckState } from '../../licensing/common/types'; + +export type { LicensingPluginStart } from '../../licensing/server'; + +export { licensingMock } from '../../licensing/server/mocks'; diff --git a/x-pack/plugins/license_api_guard/tsconfig.json b/x-pack/plugins/license_api_guard/tsconfig.json new file mode 100644 index 000000000000..1b6ea789760d --- /dev/null +++ b/x-pack/plugins/license_api_guard/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "server/**/*" + ], + "references": [ + { "path": "../licensing/tsconfig.json" }, + { "path": "../../../src/core/tsconfig.json" } + ] +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9ce17b052e3a..f5b685a609b4 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -7241,7 +7241,6 @@ "xpack.crossClusterReplication.followerIndexList.table.statusColumnTitle": "ステータス", "xpack.crossClusterReplication.homeBreadcrumbTitle": "クラスター横断レプリケーション", "xpack.crossClusterReplication.indexMgmtBadge.followerLabel": "フォロワー", - "xpack.crossClusterReplication.licenseCheckErrorMessage": "ライセンス確認失敗", "xpack.crossClusterReplication.pauseFollowerIndex.confirmModal.cancelButtonText": "キャンセル", "xpack.crossClusterReplication.pauseFollowerIndex.confirmModal.confirmButtonText": "複製を中止", "xpack.crossClusterReplication.pauseFollowerIndex.confirmModal.multiplePauseDescription": "これらのフォロワーインデックスの複製が一時停止されます:", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 4a2f682ca2f4..88639fa7246c 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7303,7 +7303,6 @@ "xpack.crossClusterReplication.followerIndexList.table.statusColumnTitle": "状态", "xpack.crossClusterReplication.homeBreadcrumbTitle": "跨集群复制", "xpack.crossClusterReplication.indexMgmtBadge.followerLabel": "Follower", - "xpack.crossClusterReplication.licenseCheckErrorMessage": "许可证检查失败", "xpack.crossClusterReplication.pauseAutoFollowPatternsLabel": "暂停{total, plural, other {复制}}", "xpack.crossClusterReplication.pauseFollowerIndex.confirmModal.cancelButtonText": "取消", "xpack.crossClusterReplication.pauseFollowerIndex.confirmModal.confirmButtonText": "暂停复制", diff --git a/x-pack/plugins/watcher/kibana.json b/x-pack/plugins/watcher/kibana.json index 695686715cb6..b9df25d80e62 100644 --- a/x-pack/plugins/watcher/kibana.json +++ b/x-pack/plugins/watcher/kibana.json @@ -5,6 +5,7 @@ "requiredPlugins": [ "home", "licensing", + "licenseApiGuard", "management", "charts", "data", diff --git a/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.test.js b/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.test.js deleted file mode 100644 index 4b39eb71cd5b..000000000000 --- a/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.test.js +++ /dev/null @@ -1,40 +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 { kibanaResponseFactory } from '../../../../../../src/core/server'; -import { licensePreRoutingFactory } from './license_pre_routing_factory'; - -describe('license_pre_routing_factory', () => { - describe('#reportingFeaturePreRoutingFactory', () => { - let mockDeps; - let licenseStatus; - - beforeEach(() => { - mockDeps = { getLicenseStatus: () => licenseStatus }; - }); - - describe('status is not valid', () => { - it('replies with 403', () => { - licenseStatus = { hasRequired: false }; - const routeWithLicenseCheck = licensePreRoutingFactory(mockDeps, () => {}); - const stubRequest = {}; - const response = routeWithLicenseCheck({}, stubRequest, kibanaResponseFactory); - expect(response.status).toBe(403); - }); - }); - - describe('status is valid', () => { - it('replies with nothing', () => { - licenseStatus = { hasRequired: true }; - const routeWithLicenseCheck = licensePreRoutingFactory(mockDeps, () => null); - const stubRequest = {}; - const response = routeWithLicenseCheck({}, stubRequest, kibanaResponseFactory); - expect(response).toBe(null); - }); - }); - }); -}); diff --git a/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts b/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts deleted file mode 100644 index d28f091f386d..000000000000 --- a/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts +++ /dev/null @@ -1,32 +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 type { KibanaRequest, KibanaResponseFactory, RequestHandler } from 'kibana/server'; -import type { RouteDependencies, WatcherRequestHandlerContext } from '../../types'; - -export const licensePreRoutingFactory = ( - { getLicenseStatus }: RouteDependencies, - handler: RequestHandler -) => { - return function licenseCheck( - ctx: Context, - request: KibanaRequest, - response: KibanaResponseFactory - ) { - const licenseStatus = getLicenseStatus(); - if (!licenseStatus.hasRequired) { - return response.customError({ - body: { - message: licenseStatus.message || '', - }, - statusCode: 403, - }); - } - - return handler(ctx, request, response); - }; -}; diff --git a/x-pack/plugins/watcher/server/plugin.ts b/x-pack/plugins/watcher/server/plugin.ts index ceade131fc5a..99ece23ef0c4 100644 --- a/x-pack/plugins/watcher/server/plugin.ts +++ b/x-pack/plugins/watcher/server/plugin.ts @@ -5,17 +5,21 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; + import { CoreSetup, + CoreStart, ILegacyCustomClusterClient, Logger, Plugin, PluginInitializerContext, } from 'kibana/server'; + import { PLUGIN, INDEX_NAMES } from '../common/constants'; import type { - Dependencies, - LicenseStatus, + SetupDependencies, + StartDependencies, RouteDependencies, WatcherRequestHandlerContext, } from './types'; @@ -28,6 +32,7 @@ import { registerWatchRoutes } from './routes/api/watch'; import { registerListFieldsRoute } from './routes/api/register_list_fields_route'; import { registerLoadHistoryRoute } from './routes/api/register_load_history_route'; import { elasticsearchJsPlugin } from './lib/elasticsearch_js_plugin'; +import { License, isEsError } from './shared_imports'; async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { const [core] = await getStartServices(); @@ -36,23 +41,20 @@ async function getCustomEsClient(getStartServices: CoreSetup['getStartServices'] } export class WatcherServerPlugin implements Plugin { - private readonly log: Logger; + private readonly license: License; + private readonly logger: Logger; private watcherESClient?: ILegacyCustomClusterClient; - private licenseStatus: LicenseStatus = { - hasRequired: false, - }; - constructor(ctx: PluginInitializerContext) { - this.log = ctx.logger.get(); + this.logger = ctx.logger.get(); + this.license = new License(); } - setup({ http, getStartServices }: CoreSetup, { licensing, features }: Dependencies) { - const router = http.createRouter(); - const routeDependencies: RouteDependencies = { - router, - getLicenseStatus: () => this.licenseStatus, - }; + setup({ http, getStartServices }: CoreSetup, { licensing, features }: SetupDependencies) { + this.license.setup({ + pluginName: PLUGIN.getI18nName(i18n), + logger: this.logger, + }); features.registerElasticsearchFeature({ id: 'watcher', @@ -90,6 +92,13 @@ export class WatcherServerPlugin implements Plugin { } ); + const router = http.createRouter(); + const routeDependencies: RouteDependencies = { + router, + license: this.license, + lib: { isEsError }, + }; + registerListFieldsRoute(routeDependencies); registerLoadHistoryRoute(routeDependencies); registerIndicesRoutes(routeDependencies); @@ -97,28 +106,15 @@ export class WatcherServerPlugin implements Plugin { registerSettingsRoutes(routeDependencies); registerWatchesRoutes(routeDependencies); registerWatchRoutes(routeDependencies); - - licensing.license$.subscribe(async (license) => { - const { state, message } = license.check(PLUGIN.ID, PLUGIN.MINIMUM_LICENSE_REQUIRED); - const hasMinimumLicense = state === 'valid'; - if (hasMinimumLicense && license.getFeature(PLUGIN.ID)) { - this.log.info('Enabling Watcher plugin.'); - this.licenseStatus = { - hasRequired: true, - }; - } else { - if (message) { - this.log.info(message); - } - this.licenseStatus = { - hasRequired: false, - message, - }; - } - }); } - start() {} + start(core: CoreStart, { licensing }: StartDependencies) { + this.license.start({ + pluginId: PLUGIN.ID, + minimumLicenseType: PLUGIN.MINIMUM_LICENSE_REQUIRED, + licensing, + }); + } stop() { if (this.watcherESClient) { diff --git a/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts b/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts index b234bed9f7d4..3b79b7b94ec8 100644 --- a/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts @@ -8,9 +8,7 @@ import { schema } from '@kbn/config-schema'; import { ILegacyScopedClusterClient } from 'kibana/server'; import { reduce, size } from 'lodash'; -import { isEsError } from '../../../shared_imports'; import { RouteDependencies } from '../../../types'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; const bodySchema = schema.object({ pattern: schema.string() }, { unknowns: 'allow' }); @@ -65,15 +63,15 @@ function getIndices(dataClient: ILegacyScopedClusterClient, pattern: string, lim }); } -export function registerGetRoute(deps: RouteDependencies) { - deps.router.post( +export function registerGetRoute({ router, license, lib: { isEsError } }: RouteDependencies) { + router.post( { path: '/api/watcher/indices', validate: { body: bodySchema, }, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { const { pattern } = request.body; try { diff --git a/x-pack/plugins/watcher/server/routes/api/license/register_refresh_route.ts b/x-pack/plugins/watcher/server/routes/api/license/register_refresh_route.ts index 0db4c7fae6a4..796494880b8e 100644 --- a/x-pack/plugins/watcher/server/routes/api/license/register_refresh_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/license/register_refresh_route.ts @@ -6,7 +6,6 @@ */ import { RouteDependencies } from '../../../types'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; /* In order for the client to have the most up-to-date snapshot of the current license, it needs to make a round-trip to the kibana server. This refresh endpoint is provided @@ -14,13 +13,13 @@ for when the client needs to check the license, but doesn't need to pull data fr server for any reason, i.e., when adding a new watch. */ -export function registerRefreshRoute(deps: RouteDependencies) { - deps.router.get( +export function registerRefreshRoute({ router, license }: RouteDependencies) { + router.get( { path: '/api/watcher/license/refresh', validate: false, }, - licensePreRoutingFactory(deps, (ctx, request, response) => { + license.guardApiRoute((ctx, request, response) => { return response.ok({ body: { success: true } }); }) ); diff --git a/x-pack/plugins/watcher/server/routes/api/register_list_fields_route.ts b/x-pack/plugins/watcher/server/routes/api/register_list_fields_route.ts index 0882fc3a6502..445249a70f0b 100644 --- a/x-pack/plugins/watcher/server/routes/api/register_list_fields_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/register_list_fields_route.ts @@ -7,10 +7,8 @@ import { schema } from '@kbn/config-schema'; import { ILegacyScopedClusterClient } from 'kibana/server'; -import { isEsError } from '../../shared_imports'; // @ts-ignore import { Fields } from '../../models/fields/index'; -import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; import { RouteDependencies } from '../../types'; const bodySchema = schema.object({ @@ -29,15 +27,19 @@ function fetchFields(dataClient: ILegacyScopedClusterClient, indexes: string[]) return dataClient.callAsCurrentUser('fieldCaps', params); } -export function registerListFieldsRoute(deps: RouteDependencies) { - deps.router.post( +export function registerListFieldsRoute({ + router, + license, + lib: { isEsError }, +}: RouteDependencies) { + router.post( { path: '/api/watcher/fields', validate: { body: bodySchema, }, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { const { indexes } = request.body; try { diff --git a/x-pack/plugins/watcher/server/routes/api/register_load_history_route.ts b/x-pack/plugins/watcher/server/routes/api/register_load_history_route.ts index 629f29734c60..67153b810c6b 100644 --- a/x-pack/plugins/watcher/server/routes/api/register_load_history_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/register_load_history_route.ts @@ -8,10 +8,8 @@ import { schema } from '@kbn/config-schema'; import { get } from 'lodash'; import { ILegacyScopedClusterClient } from 'kibana/server'; -import { isEsError } from '../../shared_imports'; import { INDEX_NAMES } from '../../../common/constants'; import { RouteDependencies } from '../../types'; -import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory'; // @ts-ignore import { WatchHistoryItem } from '../../models/watch_history_item/index'; @@ -32,15 +30,19 @@ function fetchHistoryItem(dataClient: ILegacyScopedClusterClient, watchHistoryIt }); } -export function registerLoadHistoryRoute(deps: RouteDependencies) { - deps.router.get( +export function registerLoadHistoryRoute({ + router, + license, + lib: { isEsError }, +}: RouteDependencies) { + router.get( { path: '/api/watcher/history/{id}', validate: { params: paramsSchema, }, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { const id = request.params.id; try { diff --git a/x-pack/plugins/watcher/server/routes/api/settings/register_load_route.ts b/x-pack/plugins/watcher/server/routes/api/settings/register_load_route.ts index ef5c7c6177ce..2cc1b97fb065 100644 --- a/x-pack/plugins/watcher/server/routes/api/settings/register_load_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/settings/register_load_route.ts @@ -6,11 +6,9 @@ */ import { ILegacyScopedClusterClient } from 'kibana/server'; -import { isEsError } from '../../../shared_imports'; // @ts-ignore import { Settings } from '../../../models/settings/index'; import { RouteDependencies } from '../../../types'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; function fetchClusterSettings(client: ILegacyScopedClusterClient) { return client.callAsInternalUser('cluster.getSettings', { @@ -19,13 +17,13 @@ function fetchClusterSettings(client: ILegacyScopedClusterClient) { }); } -export function registerLoadRoute(deps: RouteDependencies) { - deps.router.get( +export function registerLoadRoute({ router, license, lib: { isEsError } }: RouteDependencies) { + router.get( { path: '/api/watcher/settings', validate: false, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { try { const settings = await fetchClusterSettings(ctx.watcher!.client); return response.ok({ body: Settings.fromUpstreamJson(settings).downstreamJson }); diff --git a/x-pack/plugins/watcher/server/routes/api/watch/action/register_acknowledge_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/action/register_acknowledge_route.ts index 1afec0ada910..eb35a62dea23 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/action/register_acknowledge_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/action/register_acknowledge_route.ts @@ -8,11 +8,9 @@ import { schema } from '@kbn/config-schema'; import { get } from 'lodash'; import { ILegacyScopedClusterClient } from 'kibana/server'; -import { isEsError } from '../../../../shared_imports'; // @ts-ignore import { WatchStatus } from '../../../../models/watch_status/index'; import { RouteDependencies } from '../../../../types'; -import { licensePreRoutingFactory } from '../../../../lib/license_pre_routing_factory'; const paramsSchema = schema.object({ watchId: schema.string(), @@ -30,15 +28,19 @@ function acknowledgeAction( }); } -export function registerAcknowledgeRoute(deps: RouteDependencies) { - deps.router.put( +export function registerAcknowledgeRoute({ + router, + license, + lib: { isEsError }, +}: RouteDependencies) { + router.put( { path: '/api/watcher/watch/{watchId}/action/{actionId}/acknowledge', validate: { params: paramsSchema, }, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { const { watchId, actionId } = request.params; try { diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_activate_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_activate_route.ts index 85d1d0c51f0b..db9a4ca43d9c 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_activate_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_activate_route.ts @@ -8,9 +8,7 @@ import { schema } from '@kbn/config-schema'; import { ILegacyScopedClusterClient } from 'kibana/server'; import { get } from 'lodash'; -import { isEsError } from '../../../shared_imports'; import { RouteDependencies } from '../../../types'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; // @ts-ignore import { WatchStatus } from '../../../models/watch_status/index'; @@ -24,15 +22,15 @@ const paramsSchema = schema.object({ watchId: schema.string(), }); -export function registerActivateRoute(deps: RouteDependencies) { - deps.router.put( +export function registerActivateRoute({ router, license, lib: { isEsError } }: RouteDependencies) { + router.put( { path: '/api/watcher/watch/{watchId}/activate', validate: { params: paramsSchema, }, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { const { watchId } = request.params; try { diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_deactivate_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_deactivate_route.ts index 071c9d17beee..be012c888c3e 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_deactivate_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_deactivate_route.ts @@ -8,9 +8,7 @@ import { schema } from '@kbn/config-schema'; import { ILegacyScopedClusterClient } from 'kibana/server'; import { get } from 'lodash'; -import { isEsError } from '../../../shared_imports'; import { RouteDependencies } from '../../../types'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; // @ts-ignore import { WatchStatus } from '../../../models/watch_status/index'; @@ -24,15 +22,19 @@ function deactivateWatch(dataClient: ILegacyScopedClusterClient, watchId: string }); } -export function registerDeactivateRoute(deps: RouteDependencies) { - deps.router.put( +export function registerDeactivateRoute({ + router, + license, + lib: { isEsError }, +}: RouteDependencies) { + router.put( { path: '/api/watcher/watch/{watchId}/deactivate', validate: { params: paramsSchema, }, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { const { watchId } = request.params; try { diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_delete_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_delete_route.ts index ebf5b41bc589..0cc65a61db72 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_delete_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_delete_route.ts @@ -7,9 +7,7 @@ import { schema } from '@kbn/config-schema'; import { ILegacyScopedClusterClient } from 'kibana/server'; -import { isEsError } from '../../../shared_imports'; import { RouteDependencies } from '../../../types'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; const paramsSchema = schema.object({ watchId: schema.string(), @@ -21,15 +19,15 @@ function deleteWatch(dataClient: ILegacyScopedClusterClient, watchId: string) { }); } -export function registerDeleteRoute(deps: RouteDependencies) { - deps.router.delete( +export function registerDeleteRoute({ router, license, lib: { isEsError } }: RouteDependencies) { + router.delete( { path: '/api/watcher/watch/{watchId}', validate: { params: paramsSchema, }, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { const { watchId } = request.params; try { diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts index e2078ac5cc1d..25305b86c11c 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts @@ -8,8 +8,6 @@ import { schema } from '@kbn/config-schema'; import { ILegacyScopedClusterClient } from 'kibana/server'; import { get } from 'lodash'; -import { isEsError } from '../../../shared_imports'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; import { RouteDependencies } from '../../../types'; // @ts-ignore @@ -33,15 +31,15 @@ function executeWatch(dataClient: ILegacyScopedClusterClient, executeDetails: an }); } -export function registerExecuteRoute(deps: RouteDependencies) { - deps.router.put( +export function registerExecuteRoute({ router, license, lib: { isEsError } }: RouteDependencies) { + router.put( { path: '/api/watcher/watch/execute', validate: { body: bodySchema, }, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { const executeDetails = ExecuteDetails.fromDownstreamJson(request.body.executeDetails); const watch = Watch.fromDownstreamJson(request.body.watch); diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_history_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_history_route.ts index cafcf81511a4..b5d82647a811 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_history_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_history_route.ts @@ -10,9 +10,7 @@ import { ILegacyScopedClusterClient } from 'kibana/server'; import { get } from 'lodash'; import { fetchAllFromScroll } from '../../../lib/fetch_all_from_scroll'; import { INDEX_NAMES, ES_SCROLL_SETTINGS } from '../../../../common/constants'; -import { isEsError } from '../../../shared_imports'; import { RouteDependencies } from '../../../types'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; // @ts-ignore import { WatchHistoryItem } from '../../../models/watch_history_item/index'; @@ -50,8 +48,8 @@ function fetchHistoryItems(dataClient: ILegacyScopedClusterClient, watchId: any, .then((response: any) => fetchAllFromScroll(response, dataClient)); } -export function registerHistoryRoute(deps: RouteDependencies) { - deps.router.get( +export function registerHistoryRoute({ router, license, lib: { isEsError } }: RouteDependencies) { + router.get( { path: '/api/watcher/watch/{watchId}/history', validate: { @@ -59,7 +57,7 @@ export function registerHistoryRoute(deps: RouteDependencies) { query: querySchema, }, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { const { watchId } = request.params; const { startTime } = request.query; diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_load_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_load_route.ts index bba60cf93054..2f9321cc4c36 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_load_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_load_route.ts @@ -8,8 +8,6 @@ import { schema } from '@kbn/config-schema'; import { ILegacyScopedClusterClient } from 'kibana/server'; import { get } from 'lodash'; -import { isEsError } from '../../../shared_imports'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; // @ts-ignore import { Watch } from '../../../models/watch/index'; import { RouteDependencies } from '../../../types'; @@ -24,15 +22,15 @@ function fetchWatch(dataClient: ILegacyScopedClusterClient, watchId: string) { }); } -export function registerLoadRoute(deps: RouteDependencies) { - deps.router.get( +export function registerLoadRoute({ router, license, lib: { isEsError } }: RouteDependencies) { + router.get( { path: '/api/watcher/watch/{id}', validate: { params: paramsSchema, }, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { const id = request.params.id; try { diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts index b4a219979e65..e93ad4d04272 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts @@ -9,9 +9,7 @@ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import { WATCH_TYPES } from '../../../../common/constants'; import { serializeJsonWatch, serializeThresholdWatch } from '../../../../common/lib/serialization'; -import { isEsError } from '../../../shared_imports'; import { RouteDependencies } from '../../../types'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; const paramsSchema = schema.object({ id: schema.string(), @@ -26,8 +24,8 @@ const bodySchema = schema.object( { unknowns: 'allow' } ); -export function registerSaveRoute(deps: RouteDependencies) { - deps.router.put( +export function registerSaveRoute({ router, license, lib: { isEsError } }: RouteDependencies) { + router.put( { path: '/api/watcher/watch/{id}', validate: { @@ -35,7 +33,7 @@ export function registerSaveRoute(deps: RouteDependencies) { body: bodySchema, }, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { const { id } = request.params; const { type, isNew, isActive, ...watchConfig } = request.body; diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts index 0310d7eed9d3..d7bf3729a930 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts @@ -7,9 +7,7 @@ import { schema } from '@kbn/config-schema'; import { ILegacyScopedClusterClient } from 'kibana/server'; -import { isEsError } from '../../../shared_imports'; import { RouteDependencies } from '../../../types'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; // @ts-ignore import { Watch } from '../../../models/watch/index'; @@ -33,15 +31,15 @@ function fetchVisualizeData(dataClient: ILegacyScopedClusterClient, index: any, return dataClient.callAsCurrentUser('search', params); } -export function registerVisualizeRoute(deps: RouteDependencies) { - deps.router.post( +export function registerVisualizeRoute({ router, license, lib: { isEsError } }: RouteDependencies) { + router.post( { path: '/api/watcher/watch/visualize', validate: { body: bodySchema, }, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { const watch = Watch.fromDownstreamJson(request.body.watch); const options = VisualizeOptions.fromDownstreamJson(request.body.options); const body = watch.getVisualizeQuery(options); diff --git a/x-pack/plugins/watcher/server/routes/api/watches/register_delete_route.ts b/x-pack/plugins/watcher/server/routes/api/watches/register_delete_route.ts index 631f6fdcb090..0d837e080434 100644 --- a/x-pack/plugins/watcher/server/routes/api/watches/register_delete_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watches/register_delete_route.ts @@ -8,7 +8,6 @@ import { schema } from '@kbn/config-schema'; import { ILegacyScopedClusterClient } from 'kibana/server'; import { RouteDependencies } from '../../../types'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; const bodySchema = schema.object({ watchIds: schema.arrayOf(schema.string()), @@ -42,15 +41,15 @@ function deleteWatches(dataClient: ILegacyScopedClusterClient, watchIds: string[ }); } -export function registerDeleteRoute(deps: RouteDependencies) { - deps.router.post( +export function registerDeleteRoute({ router, license }: RouteDependencies) { + router.post( { path: '/api/watcher/watches/delete', validate: { body: bodySchema, }, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { const results = await deleteWatches(ctx.watcher!.client, request.body.watchIds); return response.ok({ body: { results } }); }) diff --git a/x-pack/plugins/watcher/server/routes/api/watches/register_list_route.ts b/x-pack/plugins/watcher/server/routes/api/watches/register_list_route.ts index 6a4e85800fa8..ef07a2b104f9 100644 --- a/x-pack/plugins/watcher/server/routes/api/watches/register_list_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watches/register_list_route.ts @@ -9,9 +9,7 @@ import { ILegacyScopedClusterClient } from 'kibana/server'; import { get } from 'lodash'; import { fetchAllFromScroll } from '../../../lib/fetch_all_from_scroll'; import { INDEX_NAMES, ES_SCROLL_SETTINGS } from '../../../../common/constants'; -import { isEsError } from '../../../shared_imports'; import { RouteDependencies } from '../../../types'; -import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; // @ts-ignore import { Watch } from '../../../models/watch/index'; @@ -30,13 +28,13 @@ function fetchWatches(dataClient: ILegacyScopedClusterClient) { .then((response: any) => fetchAllFromScroll(response, dataClient)); } -export function registerListRoute(deps: RouteDependencies) { - deps.router.get( +export function registerListRoute({ router, license, lib: { isEsError } }: RouteDependencies) { + router.get( { path: '/api/watcher/watches', validate: false, }, - licensePreRoutingFactory(deps, async (ctx, request, response) => { + license.guardApiRoute(async (ctx, request, response) => { try { const hits = await fetchWatches(ctx.watcher!.client); const watches = hits.map((hit: any) => { diff --git a/x-pack/plugins/watcher/server/shared_imports.ts b/x-pack/plugins/watcher/server/shared_imports.ts index df9b3dd53cc1..4252a2a5c32d 100644 --- a/x-pack/plugins/watcher/server/shared_imports.ts +++ b/x-pack/plugins/watcher/server/shared_imports.ts @@ -6,3 +6,4 @@ */ export { isEsError } from '../../../../src/plugins/es_ui_shared/server'; +export { License } from '../../license_api_guard/server'; diff --git a/x-pack/plugins/watcher/server/types.ts b/x-pack/plugins/watcher/server/types.ts index fccf4925b6ad..0fab4981fb41 100644 --- a/x-pack/plugins/watcher/server/types.ts +++ b/x-pack/plugins/watcher/server/types.ts @@ -7,13 +7,18 @@ import type { ILegacyScopedClusterClient, IRouter, RequestHandlerContext } from 'src/core/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; -import { LicensingPluginSetup } from '../../licensing/server'; +import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/server'; +import { License, isEsError } from './shared_imports'; -export interface Dependencies { +export interface SetupDependencies { licensing: LicensingPluginSetup; features: FeaturesPluginSetup; } +export interface StartDependencies { + licensing: LicensingPluginStart; +} + export interface ServerShim { route: any; plugins: { @@ -23,12 +28,10 @@ export interface ServerShim { export interface RouteDependencies { router: WatcherRouter; - getLicenseStatus: () => LicenseStatus; -} - -export interface LicenseStatus { - hasRequired: boolean; - message?: string; + license: License; + lib: { + isEsError: typeof isEsError; + }; } /** diff --git a/x-pack/plugins/watcher/tsconfig.json b/x-pack/plugins/watcher/tsconfig.json index e8dabe8cd40a..9f5c0db779f9 100644 --- a/x-pack/plugins/watcher/tsconfig.json +++ b/x-pack/plugins/watcher/tsconfig.json @@ -23,6 +23,7 @@ { "path": "../../../src/plugins/data/tsconfig.json" }, { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/es_ui_shared/tsconfig.json" }, + { "path": "../license_api_guard/tsconfig.json" }, { "path": "../licensing/tsconfig.json" }, { "path": "../features/tsconfig.json" }, ]