mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Extract License service from CCR and Watcher into license_api_guard plugin in x-pack (#95973)
* Localize error messages.
This commit is contained in:
parent
6faf07d0c0
commit
35214416f8
58 changed files with 433 additions and 340 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -16,6 +16,6 @@
|
|||
],
|
||||
"references": [
|
||||
{ "path": "../../core/tsconfig.json" },
|
||||
{ "path": "../data/tsconfig.json" },
|
||||
{ "path": "../data/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" },
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"requiredPlugins": [
|
||||
"home",
|
||||
"licensing",
|
||||
"licenseApiGuard",
|
||||
"management",
|
||||
"remoteClusters",
|
||||
"indexManagement",
|
||||
|
|
|
@ -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<void, void, a
|
|||
|
||||
setup(
|
||||
{ http, getStartServices }: CoreSetup,
|
||||
{ features, licensing, indexManagement, remoteClusters }: Dependencies
|
||||
{ features, licensing, indexManagement, remoteClusters }: SetupDependencies
|
||||
) {
|
||||
this.config$
|
||||
.pipe(first())
|
||||
|
@ -97,22 +96,10 @@ export class CrossClusterReplicationServerPlugin implements Plugin<void, void, a
|
|||
}
|
||||
});
|
||||
|
||||
this.license.setup(
|
||||
{
|
||||
pluginId: PLUGIN.ID,
|
||||
minimumLicenseType: PLUGIN.minimumLicenseType,
|
||||
defaultErrorMessage: i18n.translate(
|
||||
'xpack.crossClusterReplication.licenseCheckErrorMessage',
|
||||
{
|
||||
defaultMessage: 'License check failed',
|
||||
}
|
||||
),
|
||||
},
|
||||
{
|
||||
licensing,
|
||||
logger: this.logger,
|
||||
}
|
||||
);
|
||||
this.license.setup({
|
||||
pluginName: PLUGIN.TITLE,
|
||||
logger: this.logger,
|
||||
});
|
||||
|
||||
features.registerElasticsearchFeature({
|
||||
id: 'cross_cluster_replication',
|
||||
|
@ -147,7 +134,13 @@ export class CrossClusterReplicationServerPlugin implements Plugin<void, void, a
|
|||
});
|
||||
}
|
||||
|
||||
start() {}
|
||||
start(core: CoreStart, { licensing }: StartDependencies) {
|
||||
this.license.start({
|
||||
pluginId: PLUGIN.ID,
|
||||
minimumLicenseType: PLUGIN.minimumLicenseType,
|
||||
licensing,
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.ccrEsClient) {
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
|
||||
import { isEsError } from '../../../shared_imports';
|
||||
import { isEsError, License } from '../../../shared_imports';
|
||||
import { formatEsError } from '../../../lib/format_es_error';
|
||||
import { License } from '../../../services';
|
||||
import { mockRouteContext } from '../test_lib';
|
||||
import { registerCreateRoute } from './register_create_route';
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
|
||||
import { isEsError } from '../../../shared_imports';
|
||||
import { isEsError, License } from '../../../shared_imports';
|
||||
import { formatEsError } from '../../../lib/format_es_error';
|
||||
import { License } from '../../../services';
|
||||
import { mockRouteContext } from '../test_lib';
|
||||
import { registerDeleteRoute } from './register_delete_route';
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
|
||||
import { isEsError } from '../../../shared_imports';
|
||||
import { isEsError, License } from '../../../shared_imports';
|
||||
import { formatEsError } from '../../../lib/format_es_error';
|
||||
import { License } from '../../../services';
|
||||
import { mockRouteContext } from '../test_lib';
|
||||
import { registerFetchRoute } from './register_fetch_route';
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
|
||||
import { isEsError } from '../../../shared_imports';
|
||||
import { isEsError, License } from '../../../shared_imports';
|
||||
import { formatEsError } from '../../../lib/format_es_error';
|
||||
import { License } from '../../../services';
|
||||
import { mockRouteContext } from '../test_lib';
|
||||
import { registerGetRoute } from './register_get_route';
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
|
||||
import { isEsError } from '../../../shared_imports';
|
||||
import { isEsError, License } from '../../../shared_imports';
|
||||
import { formatEsError } from '../../../lib/format_es_error';
|
||||
import { License } from '../../../services';
|
||||
import { mockRouteContext } from '../test_lib';
|
||||
import { registerPauseRoute } from './register_pause_route';
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
|
||||
import { isEsError } from '../../../shared_imports';
|
||||
import { isEsError, License } from '../../../shared_imports';
|
||||
import { formatEsError } from '../../../lib/format_es_error';
|
||||
import { License } from '../../../services';
|
||||
import { mockRouteContext } from '../test_lib';
|
||||
import { registerResumeRoute } from './register_resume_route';
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
|
||||
import { isEsError } from '../../../shared_imports';
|
||||
import { isEsError, License } from '../../../shared_imports';
|
||||
import { formatEsError } from '../../../lib/format_es_error';
|
||||
import { License } from '../../../services';
|
||||
import { mockRouteContext } from '../test_lib';
|
||||
import { registerUpdateRoute } from './register_update_route';
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
|
||||
import { isEsError } from '../../../shared_imports';
|
||||
import { isEsError, License } from '../../../shared_imports';
|
||||
import { formatEsError } from '../../../lib/format_es_error';
|
||||
import { License } from '../../../services';
|
||||
import { mockRouteContext } from '../test_lib';
|
||||
import { registerCreateRoute } from './register_create_route';
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
|
||||
import { isEsError } from '../../../shared_imports';
|
||||
import { isEsError, License } from '../../../shared_imports';
|
||||
import { formatEsError } from '../../../lib/format_es_error';
|
||||
import { License } from '../../../services';
|
||||
import { mockRouteContext } from '../test_lib';
|
||||
import { registerFetchRoute } from './register_fetch_route';
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
|
||||
import { isEsError } from '../../../shared_imports';
|
||||
import { isEsError, License } from '../../../shared_imports';
|
||||
import { formatEsError } from '../../../lib/format_es_error';
|
||||
import { License } from '../../../services';
|
||||
import { mockRouteContext } from '../test_lib';
|
||||
import { registerGetRoute } from './register_get_route';
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
|
||||
import { isEsError } from '../../../shared_imports';
|
||||
import { isEsError, License } from '../../../shared_imports';
|
||||
import { formatEsError } from '../../../lib/format_es_error';
|
||||
import { License } from '../../../services';
|
||||
import { mockRouteContext } from '../test_lib';
|
||||
import { registerPauseRoute } from './register_pause_route';
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
|
||||
import { isEsError } from '../../../shared_imports';
|
||||
import { isEsError, License } from '../../../shared_imports';
|
||||
import { formatEsError } from '../../../lib/format_es_error';
|
||||
import { License } from '../../../services';
|
||||
import { mockRouteContext } from '../test_lib';
|
||||
import { registerResumeRoute } from './register_resume_route';
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
|
||||
import { kibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
|
||||
import { isEsError } from '../../../shared_imports';
|
||||
import { isEsError, License } from '../../../shared_imports';
|
||||
import { formatEsError } from '../../../lib/format_es_error';
|
||||
import { License } from '../../../services';
|
||||
import { mockRouteContext } from '../test_lib';
|
||||
import { registerUnfollowRoute } from './register_unfollow_route';
|
||||
|
||||
|
|
|
@ -5,5 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { License } from './license';
|
||||
export { addBasePath } from './add_base_path';
|
||||
|
|
|
@ -1,91 +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 { Logger } from 'src/core/server';
|
||||
import { KibanaRequest, KibanaResponseFactory, RequestHandler } from 'src/core/server';
|
||||
import type { CcrRequestHandlerContext } from '../types';
|
||||
|
||||
import { LicensingPluginSetup } from '../../../licensing/server';
|
||||
import { LicenseType } from '../../../licensing/common/types';
|
||||
|
||||
export interface LicenseStatus {
|
||||
isValid: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
interface SetupSettings {
|
||||
pluginId: string;
|
||||
minimumLicenseType: LicenseType;
|
||||
defaultErrorMessage: string;
|
||||
}
|
||||
|
||||
export class License {
|
||||
private licenseStatus: LicenseStatus = {
|
||||
isValid: false,
|
||||
message: 'Invalid License',
|
||||
};
|
||||
|
||||
private _isEsSecurityEnabled: boolean = false;
|
||||
|
||||
setup(
|
||||
{ pluginId, minimumLicenseType, defaultErrorMessage }: SetupSettings,
|
||||
{ licensing, logger }: { licensing: LicensingPluginSetup; logger: Logger }
|
||||
) {
|
||||
licensing.license$.subscribe((license) => {
|
||||
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<P, Q, B>(handler: RequestHandler<P, Q, B, CcrRequestHandlerContext>) {
|
||||
const license = this;
|
||||
|
||||
return function licenseCheck(
|
||||
ctx: CcrRequestHandlerContext,
|
||||
request: KibanaRequest<P, Q, B>,
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -6,3 +6,4 @@
|
|||
*/
|
||||
|
||||
export { isEsError } from '../../../../src/plugins/es_ui_shared/server';
|
||||
export { License } from '../../license_api_guard/server';
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" },
|
||||
]
|
||||
}
|
||||
|
|
3
x-pack/plugins/license_api_guard/READM.md
Normal file
3
x-pack/plugins/license_api_guard/READM.md
Normal file
|
@ -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.
|
|
@ -5,4 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { licensePreRoutingFactory } from './license_pre_routing_factory';
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../..',
|
||||
roots: ['<rootDir>/x-pack/plugins/license_api_guard'],
|
||||
};
|
8
x-pack/plugins/license_api_guard/kibana.json
Normal file
8
x-pack/plugins/license_api_guard/kibana.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"id": "licenseApiGuard",
|
||||
"version": "0.0.1",
|
||||
"kibanaVersion": "kibana",
|
||||
"configPath": ["xpack", "licenseApiGuard"],
|
||||
"server": true,
|
||||
"ui": false
|
||||
}
|
16
x-pack/plugins/license_api_guard/server/index.ts
Normal file
16
x-pack/plugins/license_api_guard/server/index.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 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() {}
|
||||
})();
|
||||
}
|
105
x-pack/plugins/license_api_guard/server/license.test.ts
Normal file
105
x-pack/plugins/license_api_guard/server/license.test.ts
Normal file
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
113
x-pack/plugins/license_api_guard/server/license.ts
Normal file
113
x-pack/plugins/license_api_guard/server/license.ts
Normal file
|
@ -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<Logger, 'warn'>;
|
||||
type LicenseDependency = Pick<LicensingPluginStart, 'license$'>;
|
||||
|
||||
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<Context extends RequestHandlerContext, Params, Query, Body>(
|
||||
handler: RequestHandler<Params, Query, Body, Context>
|
||||
) {
|
||||
return (
|
||||
ctx: Context,
|
||||
request: KibanaRequest<Params, Query, Body>,
|
||||
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;
|
||||
}
|
||||
}
|
12
x-pack/plugins/license_api_guard/server/shared_imports.ts
Normal file
12
x-pack/plugins/license_api_guard/server/shared_imports.ts
Normal file
|
@ -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';
|
17
x-pack/plugins/license_api_guard/tsconfig.json
Normal file
17
x-pack/plugins/license_api_guard/tsconfig.json
Normal file
|
@ -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" }
|
||||
]
|
||||
}
|
|
@ -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": "これらのフォロワーインデックスの複製が一時停止されます:",
|
||||
|
|
|
@ -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": "暂停复制",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"requiredPlugins": [
|
||||
"home",
|
||||
"licensing",
|
||||
"licenseApiGuard",
|
||||
"management",
|
||||
"charts",
|
||||
"data",
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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 = <P, Q, B, Context extends WatcherRequestHandlerContext>(
|
||||
{ getLicenseStatus }: RouteDependencies,
|
||||
handler: RequestHandler<P, Q, B, Context>
|
||||
) => {
|
||||
return function licenseCheck(
|
||||
ctx: Context,
|
||||
request: KibanaRequest<P, Q, B>,
|
||||
response: KibanaResponseFactory
|
||||
) {
|
||||
const licenseStatus = getLicenseStatus();
|
||||
if (!licenseStatus.hasRequired) {
|
||||
return response.customError({
|
||||
body: {
|
||||
message: licenseStatus.message || '',
|
||||
},
|
||||
statusCode: 403,
|
||||
});
|
||||
}
|
||||
|
||||
return handler(ctx, request, response);
|
||||
};
|
||||
};
|
|
@ -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<void, void, any, any> {
|
||||
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<WatcherRequestHandlerContext>();
|
||||
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<void, void, any, any> {
|
|||
}
|
||||
);
|
||||
|
||||
const router = http.createRouter<WatcherRequestHandlerContext>();
|
||||
const routeDependencies: RouteDependencies = {
|
||||
router,
|
||||
license: this.license,
|
||||
lib: { isEsError },
|
||||
};
|
||||
|
||||
registerListFieldsRoute(routeDependencies);
|
||||
registerLoadHistoryRoute(routeDependencies);
|
||||
registerIndicesRoutes(routeDependencies);
|
||||
|
@ -97,28 +106,15 @@ export class WatcherServerPlugin implements Plugin<void, void, any, any> {
|
|||
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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 } });
|
||||
})
|
||||
);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 } });
|
||||
})
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
*/
|
||||
|
||||
export { isEsError } from '../../../../src/plugins/es_ui_shared/server';
|
||||
export { License } from '../../license_api_guard/server';
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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" },
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue