Skip to content

Commit

Permalink
[Alerting] Fixing broken Alerts view when no Global All Kibana privil…
Browse files Browse the repository at this point in the history
…ege (#88727)

* Making kibanaFeatures an optional parameter and catching error on plugin start

* Gracefully handle 404 errors when no access to features endpoint

* Adding functional test
  • Loading branch information
ymao1 authored Jan 19, 2021
1 parent e3063f6 commit 6d1c010
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 55 deletions.
13 changes: 12 additions & 1 deletion x-pack/plugins/triggers_actions_ui/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CoreSetup, CoreStart, Plugin as CorePlugin } from 'src/core/public';
import { i18n } from '@kbn/i18n';
import { ReactElement } from 'react';
import { FeaturesPluginStart } from '../../features/public';
import { KibanaFeature } from '../../features/common';
import { registerBuiltInActionTypes } from './application/components/builtin_action_types';
import { ActionTypeModel, AlertTypeModel } from './types';
import { TypeRegistry } from './application/type_registry';
Expand Down Expand Up @@ -122,7 +123,17 @@ export class Plugin
];

const { renderApp } = await import('./application/app');
const kibanaFeatures = await pluginsStart.features.getFeatures();

// The `/api/features` endpoint requires the "Global All" Kibana privilege. Users with a
// subset of this privilege are not authorized to access this endpoint and will receive a 404
// error that causes the Alerting view to fail to load.
let kibanaFeatures: KibanaFeature[];
try {
kibanaFeatures = await pluginsStart.features.getFeatures();
} catch (err) {
kibanaFeatures = [];
}

return renderApp({
...coreStart,
data: pluginsStart.data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,87 +11,105 @@ import { getTestAlertData, getTestActionData } from '../../lib/get_test_data';

export default ({ getPageObjects, getService }: FtrProviderContext) => {
const testSubjects = getService('testSubjects');
const security = getService('security');
const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']);
const log = getService('log');
const browser = getService('browser');
const supertest = getService('supertest');
const objectRemover = new ObjectRemover(supertest);

describe('Home page', function () {
before(async () => {
await pageObjects.common.navigateToApp('triggersActions');
});
describe('Loads the app with limited privileges', () => {
before(async () => {
await security.testUser.setRoles(['alerts_and_actions_role'], true);
});
after(async () => {
await security.testUser.restoreDefaults();
});

after(async () => {
await objectRemover.removeAll();
it('Loads the Alerts page', async () => {
await pageObjects.common.navigateToApp('triggersActions');
const headingText = await pageObjects.triggersActionsUI.getSectionHeadingText();
expect(headingText).to.be('Alerts and Actions');
});
});

it('Loads the app', async () => {
await log.debug('Checking for section heading to say Triggers and Actions.');
describe('Loads the app', () => {
before(async () => {
await pageObjects.common.navigateToApp('triggersActions');
});

const headingText = await pageObjects.triggersActionsUI.getSectionHeadingText();
expect(headingText).to.be('Alerts and Actions');
});
after(async () => {
await objectRemover.removeAll();
});

it('Loads the Alerts page', async () => {
await log.debug('Checking for section heading to say Triggers and Actions.');

const headingText = await pageObjects.triggersActionsUI.getSectionHeadingText();
expect(headingText).to.be('Alerts and Actions');
});

describe('Connectors tab', () => {
it('renders the connectors tab', async () => {
// Navigate to the connectors tab
await pageObjects.triggersActionsUI.changeTabs('connectorsTab');
describe('Connectors tab', () => {
it('renders the connectors tab', async () => {
// Navigate to the connectors tab
await pageObjects.triggersActionsUI.changeTabs('connectorsTab');

await pageObjects.header.waitUntilLoadingHasFinished();
await pageObjects.header.waitUntilLoadingHasFinished();

// Verify url
const url = await browser.getCurrentUrl();
expect(url).to.contain(`/connectors`);
// Verify url
const url = await browser.getCurrentUrl();
expect(url).to.contain(`/connectors`);

// Verify content
await testSubjects.existOrFail('actionsList');
// Verify content
await testSubjects.existOrFail('actionsList');
});
});
});

describe('Alerts tab', () => {
it('renders the alerts tab', async () => {
// Navigate to the alerts tab
await pageObjects.triggersActionsUI.changeTabs('alertsTab');
describe('Alerts tab', () => {
it('renders the alerts tab', async () => {
// Navigate to the alerts tab
await pageObjects.triggersActionsUI.changeTabs('alertsTab');

await pageObjects.header.waitUntilLoadingHasFinished();
await pageObjects.header.waitUntilLoadingHasFinished();

// Verify url
const url = await browser.getCurrentUrl();
expect(url).to.contain(`/alerts`);
// Verify url
const url = await browser.getCurrentUrl();
expect(url).to.contain(`/alerts`);

// Verify content
await testSubjects.existOrFail('alertsList');
});
// Verify content
await testSubjects.existOrFail('alertsList');
});

it('navigates to an alert details page', async () => {
const { body: createdAction } = await supertest
.post(`/api/actions/action`)
.set('kbn-xsrf', 'foo')
.send(getTestActionData())
.expect(200);
objectRemover.add(createdAction.id, 'action', 'actions');
it('navigates to an alert details page', async () => {
const { body: createdAction } = await supertest
.post(`/api/actions/action`)
.set('kbn-xsrf', 'foo')
.send(getTestActionData())
.expect(200);
objectRemover.add(createdAction.id, 'action', 'actions');

const { body: createdAlert } = await supertest
.post(`/api/alerts/alert`)
.set('kbn-xsrf', 'foo')
.send(getTestAlertData())
.expect(200);
objectRemover.add(createdAlert.id, 'alert', 'alerts');
const { body: createdAlert } = await supertest
.post(`/api/alerts/alert`)
.set('kbn-xsrf', 'foo')
.send(getTestAlertData())
.expect(200);
objectRemover.add(createdAlert.id, 'alert', 'alerts');

// refresh to see alert
await browser.refresh();
// refresh to see alert
await browser.refresh();

await pageObjects.header.waitUntilLoadingHasFinished();
await pageObjects.header.waitUntilLoadingHasFinished();

// Verify content
await testSubjects.existOrFail('alertsList');
// Verify content
await testSubjects.existOrFail('alertsList');

// click on first alert
await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(createdAlert.name);
// click on first alert
await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(createdAlert.name);

// Verify url
expect(await browser.getCurrentUrl()).to.contain(`/alert/${createdAlert.id}`);
// Verify url
expect(await browser.getCurrentUrl()).to.contain(`/alert/${createdAlert.id}`);
});
});
});
});
Expand Down
16 changes: 16 additions & 0 deletions x-pack/test/functional_with_es_ssl/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,22 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
})}`,
],
},
security: {
roles: {
alerts_and_actions_role: {
kibana: [
{
feature: {
actions: ['all'],
stackAlerts: ['all'],
},
spaces: ['*'],
},
],
},
},
defaultRoles: ['superuser'],
},
};

return returnedObject;
Expand Down

0 comments on commit 6d1c010

Please sign in to comment.