Skip to content

Commit

Permalink
[Backport 4.4-7.16] Redesign the SCA table from agent's dashboard (#4748
Browse files Browse the repository at this point in the history
)

Redesign the SCA table from agent's dashboard (#4512)

* SCA table: Latest scans

* redirect and states

* params url

* the tables are working properly

* styles

* inventory test

* styles euiFlexGroup

* SCA test, back button and parameter renaming

* load for tables

* Added new policies table for reuse in agent welcome and sca

* Fixed table redirection on row clicked

* Solved review comments

* Fixed policy row redirect, renaming and remove it unused code

* Updated CHANGELOG

* Added pagination

* Updated snapshots to fix unit tests

* Updated CHANGELOG

* Fixed lastest scan navigation

Co-authored-by: Maximiliano Ibarra <maximilianoaibarra@gmail.com>
(cherry picked from commit 138893d)

Co-authored-by: Chantal Belén kelm <99441266+chantal-kelm@users.noreply.github.com>
  • Loading branch information
github-actions[bot] and chantal-kelm authored Oct 24, 2022
1 parent 22490ce commit af92d95
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 125 deletions.
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

0 comments on commit af92d95

Please sign in to comment.