[Security Solution] Add deprecation warning about rule preview permissions (#116878)
* WIP: Adding basic structure/logic for rule preview deprecation * Alert users that they need a new privilege for rule preview in 8.0 * If we find existing roles that have read access to the signals index, list their names for the user. * Refactor and unit test rule preview privilege deprecation * Wire up our deprecation handler * Fix deprecation translation Use i18n interpolation instead of a template literal. * Update signals preview with alerts-as-data index * Copy: rename "signals" -> "detection alerts" * Update tests in response to copy changes Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
9ecb189d92
commit
8a55fa4fdc
|
@ -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 { registerRulePreviewPrivilegeDeprecations } from './rule_preview_privileges';
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* 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 {
|
||||
deprecationsServiceMock,
|
||||
elasticsearchServiceMock,
|
||||
savedObjectsClientMock,
|
||||
} from 'src/core/server/mocks';
|
||||
import { RegisterDeprecationsConfig } from 'src/core/server';
|
||||
import { Role } from '../../../security/common/model';
|
||||
import {
|
||||
registerRulePreviewPrivilegeDeprecations,
|
||||
roleHasSignalsReadAccess,
|
||||
} from './rule_preview_privileges';
|
||||
|
||||
const emptyRole: Role = {
|
||||
name: 'mockRole',
|
||||
metadata: { _reserved: false },
|
||||
elasticsearch: { cluster: [], indices: [], run_as: [] },
|
||||
kibana: [{ spaces: [], base: [], feature: {} }],
|
||||
};
|
||||
|
||||
const getRoleMock = (
|
||||
indicesOverrides: Role['elasticsearch']['indices'] = [],
|
||||
name = 'mockRole'
|
||||
): Role => ({
|
||||
...emptyRole,
|
||||
name,
|
||||
elasticsearch: {
|
||||
...emptyRole.elasticsearch,
|
||||
indices: indicesOverrides,
|
||||
},
|
||||
});
|
||||
|
||||
const getDependenciesMock = () => ({
|
||||
deprecationsService: deprecationsServiceMock.createSetupContract(),
|
||||
getKibanaRoles: jest.fn(),
|
||||
packageInfo: {
|
||||
branch: 'some-branch',
|
||||
buildSha: 'deadbeef',
|
||||
dist: true,
|
||||
version: '7.16.0',
|
||||
buildNum: 1,
|
||||
},
|
||||
applicationName: 'kibana-.kibana',
|
||||
});
|
||||
|
||||
const getContextMock = () => ({
|
||||
esClient: elasticsearchServiceMock.createScopedClusterClient(),
|
||||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
});
|
||||
|
||||
describe('rule preview privileges deprecation', () => {
|
||||
describe('deprecation handler', () => {
|
||||
let mockDependencies: ReturnType<typeof getDependenciesMock>;
|
||||
let mockContext: ReturnType<typeof getContextMock>;
|
||||
let deprecationHandler: RegisterDeprecationsConfig;
|
||||
|
||||
beforeEach(() => {
|
||||
mockContext = getContextMock();
|
||||
mockDependencies = getDependenciesMock();
|
||||
registerRulePreviewPrivilegeDeprecations(mockDependencies);
|
||||
|
||||
expect(mockDependencies.deprecationsService.registerDeprecations).toHaveBeenCalledTimes(1);
|
||||
deprecationHandler =
|
||||
mockDependencies.deprecationsService.registerDeprecations.mock.calls[0][0];
|
||||
});
|
||||
|
||||
it('returns errors from getKibanaRoles', async () => {
|
||||
const errorResponse = {
|
||||
errors: [
|
||||
{
|
||||
correctiveActions: {
|
||||
manualSteps: [
|
||||
"A user with the 'manage_security' cluster privilege is required to perform this check.",
|
||||
],
|
||||
},
|
||||
level: 'fetch_error',
|
||||
message: 'Error retrieving roles for privilege deprecations: Test error',
|
||||
title: 'Error in privilege deprecations services',
|
||||
},
|
||||
],
|
||||
};
|
||||
mockDependencies.getKibanaRoles.mockResolvedValue(errorResponse);
|
||||
const result = await deprecationHandler.getDeprecations(mockContext);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
correctiveActions: {
|
||||
manualSteps: [
|
||||
"A user with the 'manage_security' cluster privilege is required to perform this check.",
|
||||
],
|
||||
},
|
||||
level: 'fetch_error',
|
||||
message: 'Error retrieving roles for privilege deprecations: Test error',
|
||||
title: 'Error in privilege deprecations services',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns an appropriate deprecation if no roles are found', async () => {
|
||||
mockDependencies.getKibanaRoles.mockResolvedValue({
|
||||
roles: [],
|
||||
});
|
||||
const result = await deprecationHandler.getDeprecations(mockContext);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
correctiveActions: {
|
||||
manualSteps: [
|
||||
'Update your roles to include read privileges for the detection alerts preview indices appropriate for that role and space(s).',
|
||||
'In 8.0, users will be unable to view preview results until those permissions are added.',
|
||||
],
|
||||
},
|
||||
deprecationType: 'feature',
|
||||
documentationUrl:
|
||||
'https://www.elastic.co/guide/en/security/some-branch/rules-ui-create.html#preview-rules',
|
||||
level: 'warning',
|
||||
message:
|
||||
'In order to enable a more robust preview, users will need read privileges to new detection alerts preview indices (.alerts-security.preview.alert-<KIBANA_SPACE>), analogous to existing detection alerts indices (.siem-signals-<KIBANA_SPACE>).',
|
||||
title: 'The Detections Rule Preview feature is changing',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns an appropriate deprecation if roles are found', async () => {
|
||||
mockDependencies.getKibanaRoles.mockResolvedValue({
|
||||
roles: [
|
||||
getRoleMock(
|
||||
[
|
||||
{
|
||||
names: ['other-index', 'second-index'],
|
||||
privileges: ['all'],
|
||||
},
|
||||
],
|
||||
'irrelevantRole'
|
||||
),
|
||||
getRoleMock(
|
||||
[
|
||||
{
|
||||
names: ['other-index', '.siem-signals-*'],
|
||||
privileges: ['all'],
|
||||
},
|
||||
],
|
||||
'relevantRole'
|
||||
),
|
||||
],
|
||||
});
|
||||
const result = await deprecationHandler.getDeprecations(mockContext);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
correctiveActions: {
|
||||
manualSteps: [
|
||||
'Update your roles to include read privileges for the detection alerts preview indices appropriate for that role and space(s).',
|
||||
'In 8.0, users will be unable to view preview results until those permissions are added.',
|
||||
'The roles that currently have read access to detection alerts indices are: relevantRole',
|
||||
],
|
||||
},
|
||||
deprecationType: 'feature',
|
||||
documentationUrl:
|
||||
'https://www.elastic.co/guide/en/security/some-branch/rules-ui-create.html#preview-rules',
|
||||
level: 'warning',
|
||||
message:
|
||||
'In order to enable a more robust preview, users will need read privileges to new detection alerts preview indices (.alerts-security.preview.alert-<KIBANA_SPACE>), analogous to existing detection alerts indices (.siem-signals-<KIBANA_SPACE>).',
|
||||
title: 'The Detections Rule Preview feature is changing',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('utilities', () => {
|
||||
describe('roleHasSignalsReadAccess', () => {
|
||||
it('returns true if the role has read privilege to all signals indexes', () => {
|
||||
const role = getRoleMock([
|
||||
{
|
||||
names: ['.siem-signals-*'],
|
||||
privileges: ['read'],
|
||||
},
|
||||
]);
|
||||
expect(roleHasSignalsReadAccess(role)).toEqual(true);
|
||||
});
|
||||
|
||||
it('returns true if the role has read privilege to a single signals index', () => {
|
||||
const role = getRoleMock([
|
||||
{
|
||||
names: ['.siem-signals-spaceId'],
|
||||
privileges: ['read'],
|
||||
},
|
||||
]);
|
||||
expect(roleHasSignalsReadAccess(role)).toEqual(true);
|
||||
});
|
||||
|
||||
it('returns true if the role has all privilege to a single signals index', () => {
|
||||
const role = getRoleMock([
|
||||
{
|
||||
names: ['.siem-signals-spaceId', 'other-index'],
|
||||
privileges: ['all'],
|
||||
},
|
||||
]);
|
||||
expect(roleHasSignalsReadAccess(role)).toEqual(true);
|
||||
});
|
||||
|
||||
it('returns false if the role has read privilege to other indices', () => {
|
||||
const role = getRoleMock([
|
||||
{
|
||||
names: ['other-index'],
|
||||
privileges: ['read'],
|
||||
},
|
||||
]);
|
||||
expect(roleHasSignalsReadAccess(role)).toEqual(false);
|
||||
});
|
||||
|
||||
it('returns false if the role has all privilege to other indices', () => {
|
||||
const role = getRoleMock([
|
||||
{
|
||||
names: ['other-index', 'second-index'],
|
||||
privileges: ['all'],
|
||||
},
|
||||
]);
|
||||
expect(roleHasSignalsReadAccess(role)).toEqual(false);
|
||||
});
|
||||
|
||||
it('returns false if the role has no specific privileges', () => {
|
||||
const role = getRoleMock();
|
||||
expect(roleHasSignalsReadAccess(role)).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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 { DeprecationsServiceSetup, PackageInfo } from 'src/core/server';
|
||||
import type { PrivilegeDeprecationsService, Role } from '../../../security/common/model';
|
||||
import { DEFAULT_SIGNALS_INDEX } from '../../common/constants';
|
||||
|
||||
const buildManualSteps = (roleNames: string[]): string[] => {
|
||||
const baseSteps = [
|
||||
i18n.translate('xpack.securitySolution.deprecations.rulePreviewPrivileges.manualStep1', {
|
||||
defaultMessage:
|
||||
'Update your roles to include read privileges for the detection alerts preview indices appropriate for that role and space(s).',
|
||||
}),
|
||||
i18n.translate('xpack.securitySolution.deprecations.rulePreviewPrivileges.manualStep2', {
|
||||
defaultMessage:
|
||||
'In 8.0, users will be unable to view preview results until those permissions are added.',
|
||||
}),
|
||||
];
|
||||
const informationalStep = i18n.translate(
|
||||
'xpack.securitySolution.deprecations.rulePreviewPrivileges.manualStep3',
|
||||
{
|
||||
defaultMessage:
|
||||
'The roles that currently have read access to detection alerts indices are: {roles}',
|
||||
values: {
|
||||
roles: roleNames.join(', '),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (roleNames.length === 0) {
|
||||
return baseSteps;
|
||||
} else {
|
||||
return [...baseSteps, informationalStep];
|
||||
}
|
||||
};
|
||||
|
||||
interface Dependencies {
|
||||
deprecationsService: DeprecationsServiceSetup;
|
||||
getKibanaRoles?: PrivilegeDeprecationsService['getKibanaRoles'];
|
||||
packageInfo: PackageInfo;
|
||||
}
|
||||
|
||||
export const registerRulePreviewPrivilegeDeprecations = ({
|
||||
deprecationsService,
|
||||
getKibanaRoles,
|
||||
packageInfo,
|
||||
}: Dependencies) => {
|
||||
deprecationsService.registerDeprecations({
|
||||
getDeprecations: async (context) => {
|
||||
let rolesWhichReadSignals: Role[] = [];
|
||||
|
||||
if (getKibanaRoles) {
|
||||
const { roles, errors } = await getKibanaRoles({ context });
|
||||
if (errors?.length) {
|
||||
return errors;
|
||||
}
|
||||
|
||||
rolesWhichReadSignals = roles?.filter(roleHasSignalsReadAccess) ?? [];
|
||||
}
|
||||
|
||||
const roleNamesWhichReadSignals = rolesWhichReadSignals.map((role) => role.name);
|
||||
|
||||
return [
|
||||
{
|
||||
title: i18n.translate('xpack.securitySolution.deprecations.rulePreviewPrivileges.title', {
|
||||
defaultMessage: 'The Detections Rule Preview feature is changing',
|
||||
}),
|
||||
message: i18n.translate(
|
||||
'xpack.securitySolution.deprecations.rulePreviewPrivileges.message',
|
||||
{
|
||||
values: {
|
||||
previewIndexPrefix: '.alerts-security.preview.alert',
|
||||
signalsIndexPrefix: DEFAULT_SIGNALS_INDEX,
|
||||
},
|
||||
defaultMessage:
|
||||
'In order to enable a more robust preview, users will need read privileges to new detection alerts preview indices ({previewIndexPrefix}-<KIBANA_SPACE>), analogous to existing detection alerts indices ({signalsIndexPrefix}-<KIBANA_SPACE>).',
|
||||
}
|
||||
),
|
||||
level: 'warning',
|
||||
deprecationType: 'feature',
|
||||
documentationUrl: `https://www.elastic.co/guide/en/security/${packageInfo.branch}/rules-ui-create.html#preview-rules`,
|
||||
correctiveActions: {
|
||||
manualSteps: buildManualSteps(roleNamesWhichReadSignals),
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const READ_PRIVILEGES = ['all', 'read'];
|
||||
|
||||
export const roleHasSignalsReadAccess = (role: Role): boolean =>
|
||||
role.elasticsearch.indices.some(
|
||||
(index) =>
|
||||
index.names.some((indexName) => indexName.startsWith(DEFAULT_SIGNALS_INDEX)) &&
|
||||
index.privileges.some((indexPrivilege) => READ_PRIVILEGES.includes(indexPrivilege))
|
||||
);
|
|
@ -70,6 +70,7 @@ import { EndpointMetadataService } from './endpoint/services/metadata';
|
|||
import { CreateRuleOptions } from './lib/detection_engine/rule_types/types';
|
||||
import { ctiFieldMap } from './lib/detection_engine/rule_types/field_maps/cti';
|
||||
import { registerPrivilegeDeprecations } from './deprecation_privileges';
|
||||
import { registerRulePreviewPrivilegeDeprecations } from './deprecations';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { legacyRulesNotificationAlertType } from './lib/detection_engine/notifications/legacy_rules_notification_alert_type';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
|
@ -335,6 +336,11 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
getKibanaRoles: plugins.security?.privilegeDeprecationsService.getKibanaRoles,
|
||||
logger: this.logger.get('deprecations'),
|
||||
});
|
||||
registerRulePreviewPrivilegeDeprecations({
|
||||
deprecationsService: core.deprecations,
|
||||
getKibanaRoles: plugins.security?.privilegeDeprecationsService.getKibanaRoles,
|
||||
packageInfo: this.pluginContext.env.packageInfo,
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue