Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] [RAC] Fix index names used by RBAC, delete hardcoded map of Kibana features to index names (#109567) #110068

Merged
merged 1 commit into from
Aug 25, 2021
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);
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;
}

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