Skip to content

Commit

Permalink
[Security Solution] Allow only users with 'all' privileges to install…
Browse files Browse the repository at this point in the history
… and upgrade prebuilt rules (elastic#161454)

Fixes: elastic#161443

## Summary

### When user doesn't have write permission:
- Disables "Add Elastic rules" button and removes Rule Updates tab

![image](https://github.com/elastic/kibana/assets/5354282/a173f18f-9b6b-4c9a-bf5f-207af13e24cb)

- Disables buttons to individually install rules, install selected rules
and install all rules

![image](https://github.com/elastic/kibana/assets/5354282/4d24d440-17f4-4d1d-96fc-4eb07914cff0)

- Disables buttons to individually upgrade rules, upgrade selected rules
and upgrade all rules

![image](https://github.com/elastic/kibana/assets/5354282/036236c1-dac0-42b8-87e5-0244d9ead281)

### `_perform` endpoints
- Returns 403 when installing all rules or specific rules

![image](https://github.com/elastic/kibana/assets/5354282/adc20409-ff09-42e5-aa33-0f1ec0df46f6)

![image](https://github.com/elastic/kibana/assets/5354282/d1faf778-d857-458e-afeb-7c573e7bf4d3)

- Returns 403 when upgrading all rules or specific rules

![image](https://github.com/elastic/kibana/assets/5354282/b21ffaa7-416c-402a-a157-12735f28e689)

![image](https://github.com/elastic/kibana/assets/5354282/b8dfecc6-4cfe-462c-9e9c-6344f59aa2d5)


### Checklist

Delete any items that are not applicable to this PR.

- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)


### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: Dmitrii <dmitrii.shevchenko@elastic.co>
  • Loading branch information
jpdjere and xcrzx authored Jul 10, 2023
1 parent 4baeafe commit 31b28a0
Show file tree
Hide file tree
Showing 8 changed files with 38 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
import React from 'react';
import { useUserData } from '../../../../../detections/components/user_info';
import { useAddPrebuiltRulesTableContext } from './add_prebuilt_rules_table_context';
import * as i18n from './translations';

Expand All @@ -15,6 +16,8 @@ export const AddPrebuiltRulesHeaderButtons = () => {
state: { rules, selectedRules, loadingRules, isRefetching, isUpgradingSecurityPackages },
actions: { installAllRules, installSelectedRules },
} = useAddPrebuiltRulesTableContext();
const [{ loading: isUserDataLoading, canUserCRUD }] = useUserData();
const canUserEditRules = canUserCRUD && !isUserDataLoading;

const isRulesAvailableForInstall = rules.length > 0;
const numberOfSelectedRules = selectedRules.length ?? 0;
Expand All @@ -29,7 +32,7 @@ export const AddPrebuiltRulesHeaderButtons = () => {
<EuiFlexItem grow={false}>
<EuiButton
onClick={installSelectedRules}
disabled={isRequestInProgress}
disabled={!canUserEditRules || isRequestInProgress}
data-test-subj="installSelectedRulesButton"
>
{i18n.INSTALL_SELECTED_RULES(numberOfSelectedRules)}
Expand All @@ -43,7 +46,7 @@ export const AddPrebuiltRulesHeaderButtons = () => {
iconType="plusInCircle"
data-test-subj="installAllRulesButton"
onClick={installAllRules}
disabled={!isRulesAvailableForInstall || isRequestInProgress}
disabled={!canUserEditRules || !isRulesAvailableForInstall || isRequestInProgress}
>
{i18n.INSTALL_ALL}
{isRuleInstalling ? <EuiLoadingSpinner size="s" /> : undefined}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import React, { useMemo } from 'react';
import { useUserData } from '../../../../detections/components/user_info';
import { TabNavigation } from '../../../../common/components/navigation/tab_navigation';
import { usePrebuiltRulesStatus } from '../../../rule_management/logic/prebuilt_rules/use_prebuilt_rules_status';
import { useRuleManagementFilters } from '../../../rule_management/logic/use_rule_management_filters';
Expand All @@ -21,26 +22,14 @@ export const RulesTableToolbar = React.memo(() => {
const { data: ruleManagementFilters } = useRuleManagementFilters();
const { data: prebuiltRulesStatus } = usePrebuiltRulesStatus();

const [{ loading, canUserCRUD }] = useUserData();

const installedTotal =
(ruleManagementFilters?.rules_summary.custom_count ?? 0) +
(ruleManagementFilters?.rules_summary.prebuilt_installed_count ?? 0);
const updateTotal = prebuiltRulesStatus?.num_prebuilt_rules_to_upgrade ?? 0;

const ruleUpdateTab = useMemo(
() => ({
[AllRulesTabs.updates]: {
id: AllRulesTabs.updates,
name: i18n.RULE_UPDATES_TAB,
disabled: false,
href: `/rules/${AllRulesTabs.updates}`,
isBeta: updateTotal > 0,
betaOptions: {
text: `${updateTotal}`,
},
},
}),
[updateTotal]
);
const shouldDisplayRuleUpdatesTab = !loading && canUserCRUD && updateTotal > 0;

const ruleTabs = useMemo(
() => ({
Expand All @@ -64,9 +53,22 @@ export const RulesTableToolbar = React.memo(() => {
text: `${installedTotal}`,
},
},
...(updateTotal > 0 ? ruleUpdateTab : {}),
...(shouldDisplayRuleUpdatesTab
? {
[AllRulesTabs.updates]: {
id: AllRulesTabs.updates,
name: i18n.RULE_UPDATES_TAB,
disabled: false,
href: `/rules/${AllRulesTabs.updates}`,
isBeta: updateTotal > 0,
betaOptions: {
text: `${updateTotal}`,
},
},
}
: {}),
}),
[installedTotal, ruleUpdateTab, updateTotal]
[installedTotal, updateTotal, shouldDisplayRuleUpdatesTab]
);

return <TabNavigation navTabs={ruleTabs} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
import React from 'react';
import { useUserData } from '../../../../../detections/components/user_info';
import * as i18n from './translations';
import { useUpgradePrebuiltRulesTableContext } from './upgrade_prebuilt_rules_table_context';

Expand All @@ -15,6 +16,8 @@ export const UpgradePrebuiltRulesTableButtons = () => {
state: { rules, selectedRules, loadingRules, isRefetching, isUpgradingSecurityPackages },
actions: { upgradeAllRules, upgradeSelectedRules },
} = useUpgradePrebuiltRulesTableContext();
const [{ loading: isUserDataLoading, canUserCRUD }] = useUserData();
const canUserEditRules = canUserCRUD && !isUserDataLoading;

const isRulesAvailableForUpgrade = rules.length > 0;
const numberOfSelectedRules = selectedRules.length ?? 0;
Expand All @@ -29,7 +32,7 @@ export const UpgradePrebuiltRulesTableButtons = () => {
<EuiFlexItem grow={false}>
<EuiButton
onClick={upgradeSelectedRules}
disabled={isRequestInProgress}
disabled={!canUserEditRules || isRequestInProgress}
data-test-subj="upgradeSelectedRulesButton"
>
<>
Expand All @@ -44,7 +47,7 @@ export const UpgradePrebuiltRulesTableButtons = () => {
fill
iconType="plusInCircle"
onClick={upgradeAllRules}
disabled={!isRulesAvailableForUpgrade || isRequestInProgress}
disabled={!canUserEditRules || !isRulesAvailableForUpgrade || isRequestInProgress}
data-test-subj="upgradeAllRulesButton"
>
{i18n.UPDATE_ALL}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const RulesPageComponent: React.FC = () => {
<SuperHeader>
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap={true}>
<EuiFlexItem grow={false}>
<AddElasticRulesButton />
<AddElasticRulesButton isDisabled={!canUserCRUD || loading} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiToolTip position="top" content={i18n.UPLOAD_VALUE_LISTS_TOOLTIP}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ import { usePrebuiltRulesStatus } from '../../../../detection_engine/rule_manage
interface AddElasticRulesButtonProps {
'data-test-subj'?: string;
fill?: boolean;
isDisabled: boolean;
showBadge?: boolean;
}

export const AddElasticRulesButton = ({
'data-test-subj': dataTestSubj = 'addElasticRulesButton',
fill,
isDisabled,
showBadge = true,
}: AddElasticRulesButtonProps) => {
const getSecuritySolutionLinkProps = useGetSecuritySolutionLinkProps();
Expand All @@ -43,6 +45,7 @@ export const AddElasticRulesButton = ({
color={'primary'}
onClick={onClickLink}
data-test-subj={dataTestSubj}
isDisabled={isDisabled}
>
{i18n.ADD_ELASTIC_RULES}
{newRulesCount > 0 && showBadge && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React, { memo } from 'react';
import styled from 'styled-components';
import { useUserData } from '../../user_info';
import { AddElasticRulesButton } from './add_elastic_rules_button';
import * as i18n from './translations';

Expand All @@ -18,6 +19,7 @@ const EmptyPrompt = styled(EuiEmptyPrompt)`
EmptyPrompt.displayName = 'EmptyPrompt';

const PrePackagedRulesPromptComponent = () => {
const [{ loading, canUserCRUD }] = useUserData();
return (
<EmptyPrompt
data-test-subj="rulesEmptyPrompt"
Expand All @@ -27,6 +29,7 @@ const PrePackagedRulesPromptComponent = () => {
<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false}>
<AddElasticRulesButton
isDisabled={!canUserCRUD || loading}
fill={true}
data-test-subj="add-elastc-rules-empty-empty-prompt-button"
showBadge={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const performRuleInstallationRoute = (router: SecuritySolutionPluginRoute
body: buildRouteValidation(PerformRuleInstallationRequestBody),
},
options: {
tags: ['access:securitySolution'],
tags: ['access:securitySolution-all'],
},
},
async (context, request, response) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const performRuleUpgradeRoute = (router: SecuritySolutionPluginRouter) =>
body: buildRouteValidation(PerformRuleUpgradeRequestBody),
},
options: {
tags: ['access:securitySolution'],
tags: ['access:securitySolution-all'],
},
},
async (context, request, response) => {
Expand Down

0 comments on commit 31b28a0

Please sign in to comment.