Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,9 @@ export const AlertConsumers = {
export type AlertConsumers = typeof AlertConsumers[keyof typeof AlertConsumers];
export type STATUS_VALUES = 'open' | 'acknowledged' | 'closed' | 'in-progress'; // TODO: remove 'in-progress' after migration to 'acknowledged'

export const mapConsumerToIndexName: Record<AlertConsumers, string | string[]> = {
apm: '.alerts-observability-apm',
logs: '.alerts-observability.logs',
infrastructure: '.alerts-observability.metrics',
observability: '.alerts-observability',
siem: '.alerts-security.alerts',
uptime: '.alerts-observability.uptime',
};
export type ValidFeatureId = keyof typeof mapConsumerToIndexName;
export type ValidFeatureId = AlertConsumers;

export const validFeatureIds = Object.keys(mapConsumerToIndexName);
Comment on lines 29 to 39
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides deleting the map itself, this PR actually fixes the names of the indices, particularly .alerts-observability-apm -> .alerts-observability.apm.alerts.
The current rule data client in the master branch writes to .alerts-observability.apm.alerts.

export const validFeatureIds = Object.values(AlertConsumers).map((v) => v as string);
export const isValidFeatureId = (a: unknown): a is ValidFeatureId =>
typeof a === 'string' && validFeatureIds.includes(a);

Expand Down
6 changes: 3 additions & 3 deletions x-pack/plugins/monitoring/common/es_glob_patterns.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const testIndices = [
'.ds-metrics-system.process.summary-default-2021.05.25-00000',
'.kibana_shahzad_9',
'.kibana-felix-log-stream_8.0.0_001',
'.kibana_smith_alerts-observability-apm-000001',
'.kibana_smith_alerts-observability.apm.alerts-000001',
'.ds-logs-endpoint.events.process-default-2021.05.26-000001',
'.kibana_dominiqueclarke54_8.0.0_001',
'.kibana-cmarcondes-19_8.0.0_001',
Expand Down Expand Up @@ -63,7 +63,7 @@ const onlySystemIndices = [
'.ds-metrics-system.process.summary-default-2021.05.25-00000',
'.kibana_shahzad_9',
'.kibana-felix-log-stream_8.0.0_001',
'.kibana_smith_alerts-observability-apm-000001',
'.kibana_smith_alerts-observability.apm.alerts-000001',
'.ds-logs-endpoint.events.process-default-2021.05.26-000001',
'.kibana_dominiqueclarke54_8.0.0_001',
'.kibana-cmarcondes-19_8.0.0_001',
Expand All @@ -85,7 +85,7 @@ const kibanaNoTaskIndices = [
'.kibana_shahzad_1',
'.kibana_shahzad_9',
'.kibana-felix-log-stream_8.0.0_001',
'.kibana_smith_alerts-observability-apm-000001',
'.kibana_smith_alerts-observability.apm.alerts-000001',
'.kibana_dominiqueclarke54_8.0.0_001',
'.kibana-cmarcondes-19_8.0.0_001',
'.kibana_dominiqueclarke55-alerts-8.0.0-000001',
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/observability/public/pages/alerts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ export function AlertsPage({ routeParams }: AlertsPageProps) {
registrationContexts: [
'observability.apm',
'observability.logs',
'observability.infrastructure',
'observability.metrics',
'observability.uptime',
],
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/observability/server/routes/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import * as t from 'io-ts';
import { Dataset } from '../../../rule_registry/server';
import { createObservabilityServerRoute } from './create_observability_server_route';
import { createObservabilityServerRouteRepository } from './create_observability_server_route_repository';

Expand All @@ -24,7 +25,7 @@ const alertsDynamicIndexPatternRoute = createObservabilityServerRoute({
const { namespace, registrationContexts } = params.query;
const indexNames = registrationContexts.flatMap((registrationContext) => {
const indexName = ruleDataService
.getRegisteredIndexInfo(registrationContext)
.findIndexByName(registrationContext, Dataset.alerts)
?.getPrimaryAlias(namespace);

if (indexName != null) {
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/rule_registry/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ await plugins.ruleRegistry.createOrUpdateComponentTemplate({
await plugins.ruleRegistry.createOrUpdateIndexTemplate({
name: plugins.ruleRegistry.getFullAssetName('apm-index-template'),
body: {
index_patterns: [plugins.ruleRegistry.getFullAssetName('observability-apm*')],
index_patterns: [plugins.ruleRegistry.getFullAssetName('observability.apm*')],
composed_of: [
// Technical component template, required
plugins.ruleRegistry.getFullAssetName(TECHNICAL_COMPONENT_TEMPLATE_NAME),
Expand All @@ -85,7 +85,7 @@ await plugins.ruleRegistry.createOrUpdateIndexTemplate({
// Finally, create the rule data client that can be injected into rule type
// executors and API endpoints
const ruleDataClient = new RuleDataClient({
alias: plugins.ruleRegistry.getFullAssetName('observability-apm'),
alias: plugins.ruleRegistry.getFullAssetName('observability.apm'),
getClusterClient: async () => {
const coreStart = await getCoreStart();
return coreStart.elasticsearch.client.asInternalUser;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,13 @@ import type {
getEsQueryConfig as getEsQueryConfigTyped,
getSafeSortIds as getSafeSortIdsTyped,
isValidFeatureId as isValidFeatureIdTyped,
mapConsumerToIndexName as mapConsumerToIndexNameTyped,
STATUS_VALUES,
ValidFeatureId,
} from '@kbn/rule-data-utils';
import {
getEsQueryConfig as getEsQueryConfigNonTyped,
getSafeSortIds as getSafeSortIdsNonTyped,
isValidFeatureId as isValidFeatureIdNonTyped,
mapConsumerToIndexName as mapConsumerToIndexNameNonTyped,
// @ts-expect-error
} from '@kbn/rule-data-utils/target_node/alerts_as_data_rbac';

Expand All @@ -42,11 +41,11 @@ import {
SPACE_IDS,
} from '../../common/technical_rule_data_field_names';
import { ParsedTechnicalFields } from '../../common/parse_technical_fields';
import { Dataset, RuleDataPluginService } from '../rule_data_plugin_service';

const getEsQueryConfig: typeof getEsQueryConfigTyped = getEsQueryConfigNonTyped;
const getSafeSortIds: typeof getSafeSortIdsTyped = getSafeSortIdsNonTyped;
const isValidFeatureId: typeof isValidFeatureIdTyped = isValidFeatureIdNonTyped;
const mapConsumerToIndexName: typeof mapConsumerToIndexNameTyped = mapConsumerToIndexNameNonTyped;

// TODO: Fix typings https://github.com/elastic/kibana/issues/101776
type NonNullableProps<Obj extends {}, Props extends keyof Obj> = Omit<Obj, Props> &
Expand All @@ -71,6 +70,7 @@ export interface ConstructorOptions {
authorization: PublicMethodsOf<AlertingAuthorization>;
auditLogger?: AuditLogger;
esClient: ElasticsearchClient;
ruleDataService: RuleDataPluginService;
}

export interface UpdateOptions<Params extends AlertTypeParams> {
Expand Down Expand Up @@ -115,15 +115,17 @@ export class AlertsClient {
private readonly authorization: PublicMethodsOf<AlertingAuthorization>;
private readonly esClient: ElasticsearchClient;
private readonly spaceId: string | undefined;
private readonly ruleDataService: RuleDataPluginService;

constructor({ auditLogger, authorization, logger, esClient }: ConstructorOptions) {
this.logger = logger;
this.authorization = authorization;
this.esClient = esClient;
this.auditLogger = auditLogger;
constructor(options: ConstructorOptions) {
this.logger = options.logger;
this.authorization = options.authorization;
this.esClient = options.esClient;
this.auditLogger = options.auditLogger;
// If spaceId is undefined, it means that spaces is disabled
// Otherwise, if space is enabled and not specified, it is "default"
this.spaceId = this.authorization.getSpaceId();
this.ruleDataService = options.ruleDataService;
}

private getOutcome(
Expand Down Expand Up @@ -666,15 +668,18 @@ export class AlertsClient {
authorizedFeatures.add(ruleType.producer);
}

const toReturn = Array.from(authorizedFeatures).flatMap((feature) => {
if (featureIds.includes(feature) && isValidFeatureId(feature)) {
if (feature === 'siem') {
return `${mapConsumerToIndexName[feature]}-${this.spaceId}`;
} else {
return `${mapConsumerToIndexName[feature]}`;
}
const validAuthorizedFeatures = Array.from(authorizedFeatures).filter(
(feature): feature is ValidFeatureId =>
featureIds.includes(feature) && isValidFeatureId(feature)
);

const toReturn = validAuthorizedFeatures.flatMap((feature) => {
const indices = this.ruleDataService.findIndicesByFeature(feature, Dataset.alerts);
if (feature === 'siem') {
return indices.map((i) => `${i.baseName}-${this.spaceId}`);
} else {
return indices.map((i) => i.baseName);
}
return [];
});

return toReturn;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { loggingSystemMock } from 'src/core/server/mocks';
import { securityMock } from '../../../security/server/mocks';
import { AuditLogger } from '../../../security/server';
import { alertingAuthorizationMock } from '../../../alerting/server/authorization/alerting_authorization.mock';
import { ruleDataPluginServiceMock } from '../rule_data_plugin_service/rule_data_plugin_service.mock';
import { RuleDataPluginService } from '../rule_data_plugin_service';

jest.mock('./alerts_client');

Expand All @@ -24,6 +26,7 @@ const alertsClientFactoryParams: AlertsClientFactoryProps = {
getAlertingAuthorization: (_: KibanaRequest) => alertingAuthMock,
securityPluginSetup,
esClient: {} as ElasticsearchClient,
ruleDataService: (ruleDataPluginServiceMock.create() as unknown) as RuleDataPluginService,
};

const fakeRequest = ({
Expand Down Expand Up @@ -64,6 +67,7 @@ describe('AlertsClientFactory', () => {
logger: alertsClientFactoryParams.logger,
auditLogger,
esClient: {},
ruleDataService: alertsClientFactoryParams.ruleDataService,
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@
* 2.0.
*/

import { ElasticsearchClient, KibanaRequest, Logger } from 'src/core/server';
import { PublicMethodsOf } from '@kbn/utility-types';
import { SecurityPluginSetup } from '../../../security/server';
import { ElasticsearchClient, KibanaRequest, Logger } from 'src/core/server';
import { AlertingAuthorization } from '../../../alerting/server';
import { SecurityPluginSetup } from '../../../security/server';
import { RuleDataPluginService } from '../rule_data_plugin_service';
import { AlertsClient } from './alerts_client';

export interface AlertsClientFactoryProps {
logger: Logger;
esClient: ElasticsearchClient;
getAlertingAuthorization: (request: KibanaRequest) => PublicMethodsOf<AlertingAuthorization>;
securityPluginSetup: SecurityPluginSetup | undefined;
ruleDataService: RuleDataPluginService | null;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we allow null here? Doesn't this together with the assertion in private ruleDataService! break type safety?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll revisit this comment tomorrow.

}

export class AlertsClientFactory {
Expand All @@ -26,6 +28,7 @@ export class AlertsClientFactory {
request: KibanaRequest
) => PublicMethodsOf<AlertingAuthorization>;
private securityPluginSetup!: SecurityPluginSetup | undefined;
private ruleDataService!: RuleDataPluginService | null;

public initialize(options: AlertsClientFactoryProps) {
/**
Expand All @@ -40,6 +43,7 @@ export class AlertsClientFactory {
this.logger = options.logger;
this.esClient = options.esClient;
this.securityPluginSetup = options.securityPluginSetup;
this.ruleDataService = options.ruleDataService;
}

public async create(request: KibanaRequest): Promise<AlertsClient> {
Expand All @@ -50,6 +54,7 @@ export class AlertsClientFactory {
authorization: getAlertingAuthorization(request),
auditLogger: securityPluginSetup?.audit.asScoped(request),
esClient: this.esClient,
ruleDataService: this.ruleDataService!,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo
import { alertingAuthorizationMock } from '../../../../alerting/server/authorization/alerting_authorization.mock';
import { AuditLogger } from '../../../../security/server';
import { AlertingAuthorizationEntity } from '../../../../alerting/server';
import { ruleDataPluginServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock';
import { RuleDataPluginService } from '../../rule_data_plugin_service';

const alertingAuthMock = alertingAuthorizationMock.create();
const esClientMock = elasticsearchClientMock.createElasticsearchClient();
Expand All @@ -30,6 +32,7 @@ const alertsClientParams: jest.Mocked<ConstructorOptions> = {
authorization: alertingAuthMock,
esClient: esClientMock,
auditLogger,
ruleDataService: (ruleDataPluginServiceMock.create() as unknown) as RuleDataPluginService,
};

const DEFAULT_SPACE = 'test_default_space_id';
Expand Down Expand Up @@ -78,7 +81,7 @@ describe('bulkUpdate()', () => {
describe('ids', () => {
describe('audit log', () => {
test('logs successful event in audit logger', async () => {
const indexName = '.alerts-observability-apm.alerts';
const indexName = '.alerts-observability.apm.alerts';
const alertsClient = new AlertsClient(alertsClientParams);
esClientMock.mget.mockResolvedValueOnce(
elasticsearchClientMock.createApiResponse({
Expand Down Expand Up @@ -107,7 +110,7 @@ describe('bulkUpdate()', () => {
{
update: {
_id: fakeAlertId,
_index: '.alerts-observability-apm.alerts',
_index: '.alerts-observability.apm.alerts',
result: 'updated',
status: 200,
},
Expand Down Expand Up @@ -135,7 +138,7 @@ describe('bulkUpdate()', () => {
});

test('audit error access if user is unauthorized for given alert', async () => {
const indexName = '.alerts-observability-apm.alerts';
const indexName = '.alerts-observability.apm.alerts';
const alertsClient = new AlertsClient(alertsClientParams);
esClientMock.mget.mockResolvedValueOnce(
elasticsearchClientMock.createApiResponse({
Expand Down Expand Up @@ -181,7 +184,7 @@ describe('bulkUpdate()', () => {
});

test('logs multiple error events in audit logger', async () => {
const indexName = '.alerts-observability-apm.alerts';
const indexName = '.alerts-observability.apm.alerts';
const alertsClient = new AlertsClient(alertsClientParams);
esClientMock.mget.mockResolvedValueOnce(
elasticsearchClientMock.createApiResponse({
Expand Down Expand Up @@ -257,7 +260,7 @@ describe('bulkUpdate()', () => {
describe('query', () => {
describe('audit log', () => {
test('logs successful event in audit logger', async () => {
const indexName = '.alerts-observability-apm.alerts';
const indexName = '.alerts-observability.apm.alerts';
const alertsClient = new AlertsClient(alertsClientParams);
esClientMock.search.mockResolvedValueOnce(
elasticsearchClientMock.createApiResponse({
Expand All @@ -276,7 +279,7 @@ describe('bulkUpdate()', () => {
hits: [
{
_id: fakeAlertId,
_index: '.alerts-observability-apm.alerts',
_index: '.alerts-observability.apm.alerts',
_source: {
[ALERT_RULE_TYPE_ID]: 'apm.error_rate',
[ALERT_RULE_CONSUMER]: 'apm',
Expand Down Expand Up @@ -317,7 +320,7 @@ describe('bulkUpdate()', () => {
});

test('audit error access if user is unauthorized for given alert', async () => {
const indexName = '.alerts-observability-apm.alerts';
const indexName = '.alerts-observability.apm.alerts';
const alertsClient = new AlertsClient(alertsClientParams);
esClientMock.search.mockResolvedValueOnce(
elasticsearchClientMock.createApiResponse({
Expand All @@ -336,7 +339,7 @@ describe('bulkUpdate()', () => {
hits: [
{
_id: fakeAlertId,
_index: '.alerts-observability-apm.alerts',
_index: '.alerts-observability.apm.alerts',
_source: {
[ALERT_RULE_TYPE_ID]: fakeRuleTypeId,
[ALERT_RULE_CONSUMER]: 'apm',
Expand Down Expand Up @@ -378,7 +381,7 @@ describe('bulkUpdate()', () => {
});

test('logs multiple error events in audit logger', async () => {
const indexName = '.alerts-observability-apm.alerts';
const indexName = '.alerts-observability.apm.alerts';
const alertsClient = new AlertsClient(alertsClientParams);
esClientMock.search.mockResolvedValueOnce(
elasticsearchClientMock.createApiResponse({
Expand All @@ -397,7 +400,7 @@ describe('bulkUpdate()', () => {
hits: [
{
_id: successfulAuthzHit,
_index: '.alerts-observability-apm.alerts',
_index: '.alerts-observability.apm.alerts',
_source: {
[ALERT_RULE_TYPE_ID]: 'apm.error_rate',
[ALERT_RULE_CONSUMER]: 'apm',
Expand All @@ -407,7 +410,7 @@ describe('bulkUpdate()', () => {
},
{
_id: unsuccessfulAuthzHit,
_index: '.alerts-observability-apm.alerts',
_index: '.alerts-observability.apm.alerts',
_source: {
[ALERT_RULE_TYPE_ID]: fakeRuleTypeId,
[ALERT_RULE_CONSUMER]: 'apm',
Expand Down
Loading