Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 4.4-2.3-wzd] Redesign the SCA table from agent's dashboard #4749

Merged
merged 1 commit into from
Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ All notable changes to the Wazuh app project will be documented in this file.

- Added the option to sort by the agents count in the group table. [#4323](https://github.com/wazuh/wazuh-kibana-app/pull/4323)
- Added agent synchronization status in the agent module. [#3874](https://github.com/wazuh/wazuh-kibana-app/pull/3874)
- Redesign the SCA table from agent's dashboard [#4512](https://github.com/wazuh/wazuh-kibana-app/pull/4512)

### Changed

Expand Down
3 changes: 2 additions & 1 deletion public/components/agents/sca/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { MainSca } from './main';
export { Inventory } from './inventory';
export { Inventory } from './inventory';
export * from './inventory/index'
187 changes: 137 additions & 50 deletions public/components/agents/sca/inventory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
EuiFlexGroup,
EuiPanel,
EuiPage,
EuiBasicTable,
EuiSpacer,
EuiText,
EuiProgress,
Expand Down Expand Up @@ -44,38 +43,49 @@ import {
import { API_NAME_AGENT_STATUS, UI_LOGGER_LEVELS } from '../../../../common/constants';
import { getErrorOrchestrator } from '../../../react-services/common-services';
import { VisualizationBasic } from '../../common/charts/visualizations/basic';
import { AppNavigate } from '../../../react-services/app-navigate';
import SCAPoliciesTable from './inventory/agent-policies-table';
import { InventoryPolicyChecksTable } from './inventory/checks-table';
import { RuleText } from './components';

type InventoryProps = {
agent: { [key: string]: any };
};

type InventoryState = {
agent: object;
itemIdToExpandedRowMap: object;
showMoreInfo: boolean;
loading: boolean;
checksIsLoading: boolean;
redirect: boolean;
filters: object[];
pageTableChecks: { pageIndex: number; pageSize?: number };
policies: object[];
lookingPolicy: { [key: string]: any } | false;
checks: object[];
lookingPolicy: { [key: string]: any } | boolean;
loadingPolicy: boolean;
secondTable: boolean;
secondTableBack: boolean;
};
export class Inventory extends Component<InventoryProps, InventoryState> {
_isMount = false;
agent: { [key: string]: any } = {};
columnsPolicies: object[];
lookingPolicy: { [key: string]: any } | false = false;
constructor(props) {
super(props);
const { agent } = this.props;
this.state = {
agent,
itemIdToExpandedRowMap: {},
showMoreInfo: false,
loading: false,
filters: [],
pageTableChecks: { pageIndex: 0 },
policies: [],
checks: [],
redirect: false,
secondTable: false,
secondTableBack: false,
checksIsLoading: false,
lookingPolicy: false,
loadingPolicy: false,
};
Expand Down Expand Up @@ -174,6 +184,13 @@ export class Inventory extends Component<InventoryProps, InventoryState> {
pageTableChecks: { pageIndex: 0, pageSize: this.state.pageTableChecks.pageSize },
});
}

const regex = new RegExp('redirectPolicyTable=' + '[^&]*');
const match = window.location.href.match(regex);
if (match && match[0] && !this.state.secondTable && !this.state.secondTableBack) {
this.loadScaPolicy(match[0].split('=')[1], true)
this.setState({secondTableBack: true, checksIsLoading: true})
}
}

componentWillUnmount() {
Expand Down Expand Up @@ -207,28 +224,31 @@ export class Inventory extends Component<InventoryProps, InventoryState> {
}
}

/**
*
* @param policy
*/
async loadScaPolicy(policy) {
handleBack (ev) {
AppNavigate.navigateToModule(ev, 'agents', { tab: 'welcome', agent: this.props.agent.id });
ev.stopPropagation();
};

async loadScaPolicy(policy, secondTable?) {
this._isMount &&
this.setState({
loadingPolicy: true,
itemIdToExpandedRowMap: {},
pageTableChecks: { pageIndex: 0 },
secondTable: secondTable ? secondTable : false
});
if (policy) {
try {
const policyResponse = await WzRequest.apiReq('GET', `/sca/${this.props.agent.id}`, {
params: {
q: 'policy_id=' + policy.policy_id,
q: 'policy_id=' + policy,
},
});
const [policyData] = policyResponse.data.data.affected_items;
this._isMount && this.setState({ lookingPolicy: policyData, loadingPolicy: false });
this._isMount &&
this.setState({ lookingPolicy: policyData, loadingPolicy: false, checksIsLoading: false });
} catch (error) {
this.setState({ lookingPolicy: policy, loadingPolicy: false });
this.setState({ lookingPolicy: policy, loadingPolicy: false, checksIsLoading: false });
const options: UIErrorLog = {
context: `${Inventory.name}.loadScaPolicy`,
level: UI_LOGGER_LEVELS.ERROR as UILogLevel,
Expand All @@ -242,16 +262,61 @@ export class Inventory extends Component<InventoryProps, InventoryState> {
getErrorOrchestrator().handleError(options);
}
} else {
this._isMount && this.setState({ lookingPolicy: policy, loadingPolicy: false, items: [] });
this._isMount && this.setState({ lookingPolicy: policy, loadingPolicy: false, items: [], checksIsLoading: false });
}
}

/**
*
* @param color
* @param title
* @param time
*/
toggleDetails = (item) => {
const itemIdToExpandedRowMap = { ...this.state.itemIdToExpandedRowMap };

if (itemIdToExpandedRowMap[item.id]) {
delete itemIdToExpandedRowMap[item.id];
} else {
let checks = '';
checks += (item.rules || []).length > 1 ? 'Checks' : 'Check';
checks += item.condition ? ` (Condition: ${item.condition})` : '';
const complianceText =
item.compliance && item.compliance.length
? item.compliance.map((el) => `${el.key}: ${el.value}`).join('\n')
: '';
const listItems = [
{
title: 'Check not applicable due to:',
description: item.reason,
},
{
title: 'Rationale',
description: item.rationale || '-',
},
{
title: 'Remediation',
description: item.remediation || '-',
},
{
title: 'Description',
description: item.description || '-',
},
{
title: (item.directory || '').includes(',') ? 'Paths' : 'Path',
description: item.directory,
},
{
title: checks,
description: <RuleText rules={item.rules.length ? item.rules : []} />,
},
{
title: 'Compliance',
description: <ComplianceText complianceText={complianceText} />,
},
];
const itemsToShow = listItems.filter((x) => {
return x.description;
});
itemIdToExpandedRowMap[item.id] = <EuiDescriptionList listItems={itemsToShow} />;
}
this.setState({ itemIdToExpandedRowMap });
};

showToast = (color, title, time) => {
getToasts().add({
color: color,
Expand Down Expand Up @@ -295,33 +360,35 @@ export class Inventory extends Component<InventoryProps, InventoryState> {
}

render() {
const getPoliciesRowProps = (item, idx) => {
return {
'data-test-subj': `sca-row-${idx}`,
className: 'customRowClass',
onClick: () => this.loadScaPolicy(item),
};
};
const { onClickRow } = this.props

const handlePoliciesTableClickRow = async (policy) => {
onClickRow ? onClickRow(policy) : await this.loadScaPolicy(policy.policy_id)
this.setState({ loading: false, redirect: true })
}

const buttonPopover = (
<EuiButtonEmpty
iconType="iInCircle"
aria-label="Help"
onClick={() => this.setState({ showMoreInfo: !this.state.showMoreInfo })}
></EuiButtonEmpty>
);
const { agent } = this.props;

return (
<Fragment>
<div>
{this.state.loading && (
{this.state.loading || this.state.checksIsLoading && (
<div style={{ margin: 16 }}>
<EuiSpacer size="m" />
<EuiProgress size="xs" color="primary" />
</div>
)}
</div>
<EuiPage>
{this.props.agent &&
(this.props.agent || {}).status !== API_NAME_AGENT_STATUS.NEVER_CONNECTED &&
{agent &&
(agent || {}).status !== API_NAME_AGENT_STATUS.NEVER_CONNECTED &&
!this.state.policies.length &&
!this.state.loading && (
<EuiCallOut title="No scans available" iconType="iInCircle">
Expand All @@ -331,8 +398,8 @@ export class Inventory extends Component<InventoryProps, InventoryState> {
</EuiCallOut>
)}

{this.props.agent &&
(this.props.agent || {}).status === API_NAME_AGENT_STATUS.NEVER_CONNECTED &&
{agent &&
(agent || {}).status === API_NAME_AGENT_STATUS.NEVER_CONNECTED &&
!this.state.loading && (
<EuiCallOut
title="Agent has never connected"
Expand All @@ -344,11 +411,11 @@ export class Inventory extends Component<InventoryProps, InventoryState> {
</EuiButton>
</EuiCallOut>
)}
{this.props.agent &&
(this.props.agent || {}).os &&
{agent &&
(agent || {}).os &&
!this.state.lookingPolicy &&
this.state.policies.length > 0 &&
!this.state.loading && (
!this.state.loading && !this.state.checksIsLoading && (
<div>
{this.state.policies.length && (
<EuiFlexGroup style={{ marginTop: 0 }}>
Expand Down Expand Up @@ -382,32 +449,32 @@ export class Inventory extends Component<InventoryProps, InventoryState> {
))}
</EuiFlexGroup>
)}
<EuiSpacer size="m" />
<EuiPanel paddingSize="l">
<EuiFlexGroup>
<EuiFlexItem>
<EuiBasicTable
items={this.state.policies}
<EuiSpacer size="m" />
<EuiFlexGroup>
<EuiFlexItem>
<EuiPanel>
<SCAPoliciesTable
agent={this.props.agent}
columns={this.columnsPolicies}
rowProps={getPoliciesRowProps}
rowProps={handlePoliciesTableClickRow}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
</div>
)}
{this.props.agent &&
(this.props.agent || {}).os &&
{agent &&
(agent || {}).os &&
this.state.lookingPolicy &&
!this.state.loading && (
((!this.state.loading) || (!this.state.checksIsLoading )) && (
<div>
<EuiPanel paddingSize="l">
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButtonIcon
color="primary"
style={{ padding: '6px 0px' }}
onClick={() => this.loadScaPolicy(false)}
onClick={this.state.secondTableBack ? (ev) => this.handleBack(ev) : () => this.loadScaPolicy(false)}
iconType="arrowLeft"
aria-label="Back to policies"
{...{ iconSize: 'l' }}
Expand Down Expand Up @@ -436,6 +503,22 @@ export class Inventory extends Component<InventoryProps, InventoryState> {
</h2>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
iconType="importAction"
onClick={async () => await this.downloadCsv()}
>
Export formatted
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
iconType="refresh"
onClick={() => this.loadScaPolicy(this.state.lookingPolicy.policy_id)}
>
Refresh
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
<EuiFlexGroup>
Expand Down Expand Up @@ -495,7 +578,7 @@ export class Inventory extends Component<InventoryProps, InventoryState> {
<EuiFlexItem>
<InventoryPolicyChecksTable
agent={this.props.agent}
lookingPolicy={this.state.lookingPolicy}
lookingPolicy={this.state.lookingPolicy}
/>
</EuiFlexItem>
</EuiFlexGroup>
Expand All @@ -507,3 +590,7 @@ export class Inventory extends Component<InventoryProps, InventoryState> {
);
}
}

Inventory.defaultProps = {
onClickRow: undefined
}
33 changes: 33 additions & 0 deletions public/components/agents/sca/inventory/agent-policies-table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { TableWzAPI } from '../../../common/tables/table-wz-api';

type Props = {
columns: any[];
rowProps?: any;
agent: { [key in string]: any };
tableProps?: any;
};

export default function SCAPoliciesTable(props: Props) {
const { columns, rowProps, agent, tableProps } = props;

const getPoliciesRowProps = (item: any, idx: string) => {
return {
'data-test-subj': `sca-row-${idx}`,
className: 'customRowClass',
onClick: rowProps ? () => rowProps(item) : null
}
}

return (
<>
<TableWzAPI
tableInitialSortingField="policy_id"
endpoint={`/sca/${agent.id}`}
tableColumns={columns}
rowProps={getPoliciesRowProps}
{ ...tableProps}
/>
</>
);
}
Loading