Skip to content

Commit

Permalink
[RAM] Add missing privilege to alerting read operations (elastic#166603)
Browse files Browse the repository at this point in the history
Closes elastic#158957

## Summary

Adds the missing `getActionErrorLog` privilege. With the updated
privileges, users with a custom Role including full access to "Actions
and Connectors", "Rule Settings" and "Stack Rules" can successfully
inspect errored actions' logs:

![Errored actions
logs](https://github.com/elastic/kibana/assets/18363145/0d34f6a3-d586-4fe7-b987-a829de0d852d)

## To Test

- Create a Role with `All` privileges granted in `Actions and
Connectors`, `Rules Settings`, `Stack Rules` (under Kibana > Management)
and assign it to a user
- Log in with that user
- Create a rule with a failing action (i.e. an Email Connector with
wrong addresses)
- Wait for the rule to execute (or execute it manually)
- In the rule page, under `History` click the number under `Errored
actions` in one of the rows of the logs table
- Check that error logs are visible in the flyout

(cherry picked from commit 0eda41a)
  • Loading branch information
umbopepato committed Sep 22, 2023
1 parent 1810987 commit 2495269
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ describe(`feature_privilege_builder`, () => {
"alerting:alert-type/my-feature/rule/getRuleState",
"alerting:alert-type/my-feature/rule/getAlertSummary",
"alerting:alert-type/my-feature/rule/getExecutionLog",
"alerting:alert-type/my-feature/rule/getActionErrorLog",
"alerting:alert-type/my-feature/rule/find",
"alerting:alert-type/my-feature/rule/getRuleExecutionKPI",
"alerting:alert-type/my-feature/rule/runSoon",
Expand Down Expand Up @@ -174,6 +175,7 @@ describe(`feature_privilege_builder`, () => {
"alerting:alert-type/my-feature/rule/getRuleState",
"alerting:alert-type/my-feature/rule/getAlertSummary",
"alerting:alert-type/my-feature/rule/getExecutionLog",
"alerting:alert-type/my-feature/rule/getActionErrorLog",
"alerting:alert-type/my-feature/rule/find",
"alerting:alert-type/my-feature/rule/getRuleExecutionKPI",
"alerting:alert-type/my-feature/rule/runSoon",
Expand Down Expand Up @@ -221,6 +223,7 @@ describe(`feature_privilege_builder`, () => {
"alerting:alert-type/my-feature/rule/getRuleState",
"alerting:alert-type/my-feature/rule/getAlertSummary",
"alerting:alert-type/my-feature/rule/getExecutionLog",
"alerting:alert-type/my-feature/rule/getActionErrorLog",
"alerting:alert-type/my-feature/rule/find",
"alerting:alert-type/my-feature/rule/getRuleExecutionKPI",
"alerting:alert-type/my-feature/rule/runSoon",
Expand Down Expand Up @@ -325,6 +328,7 @@ describe(`feature_privilege_builder`, () => {
"alerting:alert-type/my-feature/rule/getRuleState",
"alerting:alert-type/my-feature/rule/getAlertSummary",
"alerting:alert-type/my-feature/rule/getExecutionLog",
"alerting:alert-type/my-feature/rule/getActionErrorLog",
"alerting:alert-type/my-feature/rule/find",
"alerting:alert-type/my-feature/rule/getRuleExecutionKPI",
"alerting:alert-type/my-feature/rule/runSoon",
Expand Down Expand Up @@ -389,6 +393,7 @@ describe(`feature_privilege_builder`, () => {
"alerting:alert-type/my-feature/rule/getRuleState",
"alerting:alert-type/my-feature/rule/getAlertSummary",
"alerting:alert-type/my-feature/rule/getExecutionLog",
"alerting:alert-type/my-feature/rule/getActionErrorLog",
"alerting:alert-type/my-feature/rule/find",
"alerting:alert-type/my-feature/rule/getRuleExecutionKPI",
"alerting:alert-type/my-feature/rule/runSoon",
Expand All @@ -412,6 +417,7 @@ describe(`feature_privilege_builder`, () => {
"alerting:readonly-alert-type/my-feature/rule/getRuleState",
"alerting:readonly-alert-type/my-feature/rule/getAlertSummary",
"alerting:readonly-alert-type/my-feature/rule/getExecutionLog",
"alerting:readonly-alert-type/my-feature/rule/getActionErrorLog",
"alerting:readonly-alert-type/my-feature/rule/find",
"alerting:readonly-alert-type/my-feature/rule/getRuleExecutionKPI",
"alerting:readonly-alert-type/my-feature/rule/runSoon",
Expand Down Expand Up @@ -504,6 +510,7 @@ describe(`feature_privilege_builder`, () => {
"alerting:alert-type/my-feature/rule/getRuleState",
"alerting:alert-type/my-feature/rule/getAlertSummary",
"alerting:alert-type/my-feature/rule/getExecutionLog",
"alerting:alert-type/my-feature/rule/getActionErrorLog",
"alerting:alert-type/my-feature/rule/find",
"alerting:alert-type/my-feature/rule/getRuleExecutionKPI",
"alerting:alert-type/my-feature/rule/runSoon",
Expand All @@ -527,6 +534,7 @@ describe(`feature_privilege_builder`, () => {
"alerting:readonly-alert-type/my-feature/rule/getRuleState",
"alerting:readonly-alert-type/my-feature/rule/getAlertSummary",
"alerting:readonly-alert-type/my-feature/rule/getExecutionLog",
"alerting:readonly-alert-type/my-feature/rule/getActionErrorLog",
"alerting:readonly-alert-type/my-feature/rule/find",
"alerting:readonly-alert-type/my-feature/rule/getRuleExecutionKPI",
"alerting:readonly-alert-type/my-feature/rule/runSoon",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const readOperations: Record<AlertingEntity, string[]> = {
'getRuleState',
'getAlertSummary',
'getExecutionLog',
'getActionErrorLog',
'find',
'getRuleExecutionKPI',
'runSoon',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@
import expect from '@kbn/expect';

import { ESTestIndexTool } from '@kbn/alerting-api-integration-helpers';
import { Spaces } from '../../../scenarios';
import { getUrlPrefix, ObjectRemover, getTestRuleData, getEventLog } from '../../../../common/lib';
import { Spaces, UserAtSpaceScenarios } from '../../../scenarios';
import {
getUrlPrefix,
ObjectRemover,
getTestRuleData,
getEventLog,
getConsumerUnauthorizedErrorMessage,
} from '../../../../common/lib';
import { FtrProviderContext } from '../../../../common/ftr_provider_context';

// eslint-disable-next-line import/no-default-export
export default function createGetActionErrorLogTests({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const supertestWithoutAuth = getService('supertestWithoutAuth');
const retry = getService('retry');
const es = getService('es');
const esTestIndexTool = new ESTestIndexTool(es, retry);
Expand All @@ -33,6 +40,98 @@ export default function createGetActionErrorLogTests({ getService }: FtrProvider
await objectRemover.removeAll();
});

for (const scenario of UserAtSpaceScenarios) {
const { user, space } = scenario;
describe(scenario.id, () => {
it('gets action error logs for rules with action errors with appropriate authorization', async () => {
const { body: createdConnector } = await supertest
.post(`${getUrlPrefix(space.id)}/api/actions/connector`)
.set('kbn-xsrf', 'foo')
.send({
name: 'connector that throws',
connector_type_id: 'test.throw',
config: {},
secrets: {},
})
.expect(200);
objectRemover.add(space.id, createdConnector.id, 'action', 'actions');

const { body: createdRule } = await supertest
.post(`${getUrlPrefix(space.id)}/api/alerting/rule`)
.set('kbn-xsrf', 'foo')
.send(
getTestRuleData({
rule_type_id: 'test.cumulative-firing',
actions: [
{
id: createdConnector.id,
group: 'default',
params: {},
},
],
})
)
.expect(200);
objectRemover.add(space.id, createdRule.id, 'rule', 'alerting');

await waitForEvents(
createdRule.id,
'alerting',
new Map([['execute', { gte: 1 }]]),
space.id
);
await waitForEvents(
createdRule.id,
'actions',
new Map([['execute', { gte: 1 }]]),
space.id
);

const response = await supertestWithoutAuth
.get(
`${getUrlPrefix(space.id)}/internal/alerting/rule/${
createdRule.id
}/_action_error_log?date_start=${dateStart}`
)
.auth(user.username, user.password);

switch (scenario.id) {
case 'no_kibana_privileges at space1':
case 'space_1_all at space2':
expect(response.statusCode).to.eql(403);
expect(response.body).to.eql({
error: 'Forbidden',
message: getConsumerUnauthorizedErrorMessage(
'get',
'test.cumulative-firing',
'alertsFixture'
),
statusCode: 403,
});
break;
case 'global_read at space1':
case 'superuser at space1':
case 'space_1_all at space1':
case 'space_1_all_alerts_none_actions at space1':
case 'space_1_all_with_restricted_fixture at space1':
expect(response.statusCode).to.eql(200);
expect(response.body.totalErrors).to.eql(1);
expect(response.body.errors.length).to.eql(1);

for (const errors of response.body.errors) {
expect(errors.type).to.equal('actions');
expect(errors.message).to.equal(
`action execution failure: test.throw:${createdConnector.id}: connector that throws - an error occurred while running the action: this action is intended to fail; retry: true`
);
}
break;
default:
throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`);
}
});
});
}

it('gets action error logs from an alternate space', async () => {
const { body: createdConnector } = await supertest
.post(`${getUrlPrefix(Spaces[1].id)}/api/actions/connector`)
Expand Down Expand Up @@ -99,12 +198,13 @@ export default function createGetActionErrorLogTests({ getService }: FtrProvider
{
gte: number;
}
>
>,
spaceId = Spaces[1].id
) {
await retry.try(async () => {
return await getEventLog({
getService,
spaceId: Spaces[1].id,
spaceId,
type: 'alert',
id,
provider,
Expand Down

0 comments on commit 2495269

Please sign in to comment.