diff --git a/x-pack/plugins/apm/server/feature.ts b/x-pack/plugins/apm/server/feature.ts index 8a14924c8aafaf..51f82a577d429b 100644 --- a/x-pack/plugins/apm/server/feature.ts +++ b/x-pack/plugins/apm/server/feature.ts @@ -31,7 +31,7 @@ export const APM_FEATURE = { privileges: { all: { app: ['apm', 'ux', 'kibana'], - api: ['apm', 'apm_write'], + api: ['apm', 'apm_write', 'rac'], catalogue: ['apm'], savedObject: { all: [], @@ -52,7 +52,7 @@ export const APM_FEATURE = { }, read: { app: ['apm', 'ux', 'kibana'], - api: ['apm'], + api: ['apm', 'rac'], catalogue: ['apm'], savedObject: { all: [], @@ -74,14 +74,21 @@ export const APM_FEATURE = { }, subFeatures: [ { - name: 'Manage Alerts', + name: i18n.translate('xpack.apm.featureRegistry.manageAlerts', { + defaultMessage: 'Manage Alerts', + }), privilegeGroups: [ { groupType: 'independent', privileges: [ { id: 'alert_manage', - name: 'Manage Alerts', + name: i18n.translate( + 'xpack.apm.featureRegistry.subfeature.apmFeatureName', + { + defaultMessage: 'Manage Alerts', + } + ), includeIn: 'all', alerting: { alert: { diff --git a/x-pack/plugins/rule_registry/common/constants.ts b/x-pack/plugins/rule_registry/common/constants.ts new file mode 100644 index 00000000000000..d9aba65e4373e8 --- /dev/null +++ b/x-pack/plugins/rule_registry/common/constants.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const BASE_RAC_ALERTS_API_PATH = '/api/rac/alerts'; diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alert_client.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alert_client.ts index 07a0440679cde8..e0d75dc07afc1c 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alert_client.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alert_client.ts @@ -20,7 +20,7 @@ import { AlertingAuthorizationEntity, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../alerting/server/authorization'; -import { Logger, ElasticsearchClient } from '../../../../../src/core/server'; +import { Logger, ElasticsearchClient, HttpResponsePayload } from '../../../../../src/core/server'; import { buildAlertsSearchQuery, buildAlertsUpdateParameters } from './utils'; import { RacAuthorizationAuditLogger } from './audit_logger'; @@ -108,7 +108,8 @@ export class AlertsClient { this.auditLogger = auditLogger; } - public async get({ id }: GetAlertParams): Promise { + // TODO: Type out alerts (rule registry fields + alerting alerts type) + public async get({ id }: GetAlertParams): Promise { // first search for the alert specified, then check if user has access to it // and return search results const query = buildAlertsSearchQuery({ @@ -117,20 +118,21 @@ export class AlertsClient { }); // TODO: Type out alerts (rule registry fields + alerting alerts type) const { body: result } = await this.esClient.search(query); - const hits = result.hits.hits[0]; + // TODO: I thought we were moving away from using _source? + const hits = result.hits.hits[0]._source; try { // use security plugin routes to check what URIs user is authorized to await this.authorization.ensureAuthorized({ - ruleTypeId: hits['kibana.rac.alert.id'], - consumer: hits['kibana.rac.producer'], + ruleTypeId: hits['rule.id'], + consumer: hits['kibana.rac.alert.producer'], operation: ReadOperations.Get, entity: AlertingAuthorizationEntity.Alert, }); } catch (error) { throw Boom.forbidden( this.auditLogger.racAuthorizationFailure({ - owner: hits['kibana.rac.producer'], + owner: hits['kibana.rac.alert.producer'], operation: ReadOperations.Get, type: 'access', }) diff --git a/x-pack/plugins/rule_registry/server/plugin.ts b/x-pack/plugins/rule_registry/server/plugin.ts index d15525cbb6e319..a8dff3d0d49d2b 100644 --- a/x-pack/plugins/rule_registry/server/plugin.ts +++ b/x-pack/plugins/rule_registry/server/plugin.ts @@ -21,6 +21,7 @@ import { AlertsClientFactory } from './alert_data_client/alert_client_factory'; import { PluginStartContract as AlertingStart } from '../../alerting/server'; import { SpacesPluginStart } from '../../spaces/server'; import { RacApiRequestHandlerContext, RacRequestHandlerContext } from './types'; +import { defineRoutes } from './routes'; export type RuleRegistryPluginSetupContract = RuleDataPluginService; export type RuleRegistryPluginStartContract = void; @@ -70,11 +71,14 @@ export class RuleRegistryPlugin implements Plugin(); core.http.registerRouteHandlerContext( 'rac', this.createRouteHandlerContext() ); + defineRoutes(router); + return service; } @@ -111,7 +115,7 @@ export class RuleRegistryPlugin implements Plugin { return { - getRacClient: async () => { + getAlertsClient: async () => { const createdClient = alertsClientFactory.create(request); return createdClient; }, diff --git a/x-pack/plugins/rule_registry/server/routes/get_alert_by_id.ts b/x-pack/plugins/rule_registry/server/routes/get_alert_by_id.ts new file mode 100644 index 00000000000000..62b4e1cc4dd520 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/routes/get_alert_by_id.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from 'kibana/server'; +import * as t from 'io-ts'; +import { id as _id } from '@kbn/securitysolution-io-ts-list-types'; + +import { RacRequestHandlerContext } from '../types'; +import { BASE_RAC_ALERTS_API_PATH } from '../../common/constants'; +import { buildRouteValidation } from './utils/route_validation'; + +export const getAlertByIdRoute = (router: IRouter) => { + router.get( + { + path: BASE_RAC_ALERTS_API_PATH, + validate: { + query: buildRouteValidation( + t.exact( + t.type({ + id: _id, + }) + ) + ), + }, + options: { + tags: ['access:rac'], + }, + }, + async (context, request, response) => { + const alertsClient = await context.rac.getAlertsClient(); + const { id } = request.query; + const alert = await alertsClient.get({ id }); + return response.ok({ + body: alert, + }); + } + ); +}; diff --git a/x-pack/plugins/rule_registry/server/routes/index.ts b/x-pack/plugins/rule_registry/server/routes/index.ts new file mode 100644 index 00000000000000..84b2205ef0bc8c --- /dev/null +++ b/x-pack/plugins/rule_registry/server/routes/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from 'kibana/server'; +import { RacRequestHandlerContext } from '../types'; +import { getAlertByIdRoute } from './get_alert_by_id'; + +export function defineRoutes(router: IRouter) { + getAlertByIdRoute(router); +} diff --git a/x-pack/plugins/rule_registry/server/routes/utils/route_validation.ts b/x-pack/plugins/rule_registry/server/routes/utils/route_validation.ts new file mode 100644 index 00000000000000..8e74760d6d15f3 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/routes/utils/route_validation.ts @@ -0,0 +1,56 @@ +/* + * 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. + */ +/* + * 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 { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import * as rt from 'io-ts'; +import { exactCheck, formatErrors } from '@kbn/securitysolution-io-ts-utils'; + +import { + RouteValidationError, + RouteValidationFunction, + RouteValidationResultFactory, +} from '../../../../../../src/core/server'; + +type RequestValidationResult = + | { + value: T; + error?: undefined; + } + | { + value?: undefined; + error: RouteValidationError; + }; + +/** + * Copied from x-pack/plugins/security_solution/server/utils/build_validation/route_validation.ts + * This really should be in @kbn/securitysolution-io-ts-utils rather than copied yet again, however, this has types + * from a lot of places such as RouteValidationResultFactory from core/server which in turn can pull in @kbn/schema + * which cannot work on the front end and @kbn/securitysolution-io-ts-utils works on both front and backend. + * + * TODO: Figure out a way to move this function into a package rather than copying it/forking it within plugins + */ +export const buildRouteValidation = >( + schema: T +): RouteValidationFunction => ( + inputValue: unknown, + validationResult: RouteValidationResultFactory +): RequestValidationResult => + pipe( + schema.decode(inputValue), + (decoded) => exactCheck(inputValue, decoded), + fold>( + (errors: rt.Errors) => validationResult.badRequest(formatErrors(errors).join()), + (validatedInput: A) => validationResult.ok(validatedInput) + ) + ); diff --git a/x-pack/plugins/rule_registry/server/scripts/get_alert_by_id.sh b/x-pack/plugins/rule_registry/server/scripts/get_alert_by_id.sh new file mode 100755 index 00000000000000..fc4bb6e2a0c429 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/scripts/get_alert_by_id.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# 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. +# + +set -e + +# Example: ./get_alert_by_id.sh {id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/rac/alerts?id="$1" | jq . diff --git a/x-pack/plugins/rule_registry/server/types.ts b/x-pack/plugins/rule_registry/server/types.ts index 01af988222275e..4ec4ca1afff026 100644 --- a/x-pack/plugins/rule_registry/server/types.ts +++ b/x-pack/plugins/rule_registry/server/types.ts @@ -45,12 +45,12 @@ export type AlertTypeWithExecutor< * @public */ export interface RacApiRequestHandlerContext { - getRacClient: () => Promise; + getAlertsClient: () => Promise; } /** * @internal */ export interface RacRequestHandlerContext extends RequestHandlerContext { - rac?: RacApiRequestHandlerContext; + rac: RacApiRequestHandlerContext; } diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index aa37a0dc1f6270..9a35670737779c 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -227,7 +227,7 @@ export class Plugin implements IPlugin