[RAC] Disable the actions button if the user has inadequate privileges (#117488) (#117624)

Disable the actions button if the user has inadequate privileges

Co-authored-by: Ersin Erdal <92688503+ersin-erdal@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2021-11-05 07:18:46 -04:00 committed by GitHub
parent 7645d475b6
commit 66b6d2b406
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 134 additions and 75 deletions

View file

@ -38,7 +38,6 @@ import {
EuiPopover,
EuiToolTip,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import styled from 'styled-components';
import React, { Suspense, useMemo, useState, useCallback, useEffect } from 'react';
import usePrevious from 'react-use/lib/usePrevious';
@ -66,6 +65,7 @@ import { getDefaultCellActions } from './default_cell_actions';
import { LazyAlertsFlyout } from '../..';
import { parseAlert } from './parse_alert';
import { CoreStart } from '../../../../../../src/core/public';
import { translations } from './translations';
const ALERT_DURATION: typeof ALERT_DURATION_TYPED = ALERT_DURATION_NON_TYPED;
const ALERT_REASON: typeof ALERT_REASON_TYPED = ALERT_REASON_NON_TYPED;
@ -115,33 +115,25 @@ export const columns: Array<
> = [
{
columnHeaderType: 'not-filtered',
displayAsText: i18n.translate('xpack.observability.alertsTGrid.statusColumnDescription', {
defaultMessage: 'Alert Status',
}),
displayAsText: translations.statusColumnDescription,
id: ALERT_STATUS,
initialWidth: 110,
},
{
columnHeaderType: 'not-filtered',
displayAsText: i18n.translate('xpack.observability.alertsTGrid.lastUpdatedColumnDescription', {
defaultMessage: 'Last updated',
}),
displayAsText: translations.lastUpdatedColumnDescription,
id: TIMESTAMP,
initialWidth: 230,
},
{
columnHeaderType: 'not-filtered',
displayAsText: i18n.translate('xpack.observability.alertsTGrid.durationColumnDescription', {
defaultMessage: 'Duration',
}),
displayAsText: translations.durationColumnDescription,
id: ALERT_DURATION,
initialWidth: 116,
},
{
columnHeaderType: 'not-filtered',
displayAsText: i18n.translate('xpack.observability.alertsTGrid.reasonColumnDescription', {
defaultMessage: 'Reason',
}),
displayAsText: translations.reasonColumnDescription,
id: ALERT_REASON,
linkField: '*',
},
@ -249,73 +241,61 @@ function ObservabilityActions({
];
}, [afterCaseSelection, casePermissions, timelines, event, statusActionItems, alertPermissions]);
const viewDetailsTextLabel = i18n.translate(
'xpack.observability.alertsTable.viewDetailsTextLabel',
{
defaultMessage: 'View details',
}
);
const viewInAppTextLabel = i18n.translate('xpack.observability.alertsTable.viewInAppTextLabel', {
defaultMessage: 'View in app',
});
const moreActionsTextLabel = i18n.translate(
'xpack.observability.alertsTable.moreActionsTextLabel',
{
defaultMessage: 'More actions',
}
);
const actionsToolTip =
actionsMenuItems.length <= 0
? translations.notEnoughPermissions
: translations.moreActionsTextLabel;
return (
<>
<EuiFlexGroup gutterSize="none" responsive={false}>
<EuiFlexItem>
<EuiToolTip content={viewDetailsTextLabel}>
<EuiToolTip content={translations.viewDetailsTextLabel}>
<EuiButtonIcon
size="s"
iconType="expand"
color="text"
onClick={() => setFlyoutAlert(alert)}
data-test-subj="openFlyoutButton"
aria-label={viewDetailsTextLabel}
aria-label={translations.viewDetailsTextLabel}
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem>
<EuiToolTip content={viewInAppTextLabel}>
<EuiToolTip content={translations.viewInAppTextLabel}>
<EuiButtonIcon
size="s"
href={prepend(alert.link ?? '')}
iconType="eye"
color="text"
aria-label={viewInAppTextLabel}
aria-label={translations.viewInAppTextLabel}
/>
</EuiToolTip>
</EuiFlexItem>
{actionsMenuItems.length > 0 && (
<EuiFlexItem>
<EuiPopover
button={
<EuiToolTip content={moreActionsTextLabel}>
<EuiButtonIcon
display="empty"
size="s"
color="text"
iconType="boxesHorizontal"
aria-label={moreActionsTextLabel}
onClick={() => toggleActionsPopover(eventId)}
data-test-subj="alerts-table-row-action-more"
/>
</EuiToolTip>
}
isOpen={openActionsPopoverId === eventId}
closePopover={closeActionsPopover}
panelPaddingSize="none"
anchorPosition="downLeft"
>
<EuiContextMenuPanel size="s" items={actionsMenuItems} />
</EuiPopover>
</EuiFlexItem>
)}
<EuiFlexItem>
<EuiPopover
button={
<EuiToolTip content={actionsToolTip}>
<EuiButtonIcon
display="empty"
disabled={actionsMenuItems.length <= 0}
size="s"
color="text"
iconType="boxesHorizontal"
aria-label={actionsToolTip}
onClick={() => toggleActionsPopover(eventId)}
data-test-subj="alerts-table-row-action-more"
/>
</EuiToolTip>
}
isOpen={openActionsPopoverId === eventId}
closePopover={closeActionsPopover}
panelPaddingSize="none"
anchorPosition="downLeft"
>
<EuiContextMenuPanel size="s" items={actionsMenuItems} />
</EuiPopover>
</EuiFlexItem>
</EuiFlexGroup>
</>
);
@ -363,13 +343,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
id: 'expand',
width: 120,
headerCellRender: () => {
return (
<EventsThContent>
{i18n.translate('xpack.observability.alertsTable.actionsTextLabel', {
defaultMessage: 'Actions',
})}
</EventsThContent>
);
return <EventsThContent>{translations.actionsTextLabel}</EventsThContent>;
},
rowCellRender: (actionProps: ActionProps) => {
return (
@ -400,12 +374,8 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
hasAlertsCrudPermissions,
indexNames,
itemsPerPageOptions: [10, 25, 50],
loadingText: i18n.translate('xpack.observability.alertsTable.loadingTextLabel', {
defaultMessage: 'loading alerts',
}),
footerText: i18n.translate('xpack.observability.alertsTable.footerTextLabel', {
defaultMessage: 'alerts',
}),
loadingText: translations.loadingTextLabel,
footerText: translations.footerTextLabel,
query: {
query: `${ALERT_WORKFLOW_STATUS}: ${workflowStatus}${kuery !== '' ? ` and ${kuery}` : ''}`,
language: 'kuery',
@ -424,11 +394,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
filterStatus: workflowStatus as AlertWorkflowStatus,
leadingControlColumns,
trailingControlColumns,
unit: (totalAlerts: number) =>
i18n.translate('xpack.observability.alertsTable.showingAlertsTitle', {
values: { totalAlerts },
defaultMessage: '{totalAlerts, plural, =1 {alert} other {alerts}}',
}),
unit: (totalAlerts: number) => translations.showingAlertsTitle(totalAlerts),
};
}, [
casePermissions,
@ -443,6 +409,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
leadingControlColumns,
deletedEventIds,
]);
const handleFlyoutClose = () => setFlyoutAlert(undefined);
const { observabilityRuleTypeRegistry } = usePluginContext();

View file

@ -0,0 +1,61 @@
/*
* 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';
export const translations = {
viewDetailsTextLabel: i18n.translate('xpack.observability.alertsTable.viewDetailsTextLabel', {
defaultMessage: 'View details',
}),
viewInAppTextLabel: i18n.translate('xpack.observability.alertsTable.viewInAppTextLabel', {
defaultMessage: 'View in app',
}),
moreActionsTextLabel: i18n.translate('xpack.observability.alertsTable.moreActionsTextLabel', {
defaultMessage: 'More actions',
}),
notEnoughPermissions: i18n.translate('xpack.observability.alertsTable.notEnoughPermissions', {
defaultMessage: 'Additional privileges required',
}),
statusColumnDescription: i18n.translate(
'xpack.observability.alertsTGrid.statusColumnDescription',
{
defaultMessage: 'Alert Status',
}
),
lastUpdatedColumnDescription: i18n.translate(
'xpack.observability.alertsTGrid.lastUpdatedColumnDescription',
{
defaultMessage: 'Last updated',
}
),
durationColumnDescription: i18n.translate(
'xpack.observability.alertsTGrid.durationColumnDescription',
{
defaultMessage: 'Duration',
}
),
reasonColumnDescription: i18n.translate(
'xpack.observability.alertsTGrid.reasonColumnDescription',
{
defaultMessage: 'Reason',
}
),
actionsTextLabel: i18n.translate('xpack.observability.alertsTable.actionsTextLabel', {
defaultMessage: 'Actions',
}),
loadingTextLabel: i18n.translate('xpack.observability.alertsTable.loadingTextLabel', {
defaultMessage: 'loading alerts',
}),
footerTextLabel: i18n.translate('xpack.observability.alertsTable.footerTextLabel', {
defaultMessage: 'alerts',
}),
showingAlertsTitle: (totalAlerts: number) =>
i18n.translate('xpack.observability.alertsTable.showingAlertsTitle', {
values: { totalAlerts },
defaultMessage: '{totalAlerts, plural, =1 {alert} other {alerts}}',
}),
};

View file

@ -208,6 +208,13 @@ export function ObservabilityAlertsCommonProvider({
return buttonText.substring(0, buttonText.indexOf('\n'));
};
const getActionsButtonByIndex = async (index: number) => {
const actionsOverflowButtons = await find.allByCssSelector(
'[data-test-subj="alerts-table-row-action-more"]'
);
return actionsOverflowButtons[index] || null;
};
return {
getQueryBar,
clearQueryBar,
@ -236,5 +243,6 @@ export function ObservabilityAlertsCommonProvider({
typeInQueryBar,
openActionsMenuForRow,
getTimeRange,
getActionsButtonByIndex,
};
}

View file

@ -214,5 +214,28 @@ export default ({ getService }: FtrProviderContext) => {
});
});
});
describe('Actions Button', () => {
before(async () => {
await observability.users.setTestUserRole(
observability.users.defineBasicObservabilityRole({
observabilityCases: ['read'],
logs: ['read'],
})
);
await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs');
await observability.alerts.common.navigateToTimeWithData();
});
after(async () => {
await observability.users.restoreDefaultTestUserRole();
await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs');
});
it('Is disabled when a user has only read privilages', async () => {
const actionsButton = await observability.alerts.common.getActionsButtonByIndex(0);
expect(await actionsButton.getAttribute('disabled')).to.be('true');
});
});
});
};