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

Redesign the SCA table from agent's dashboard #4512

Merged
merged 23 commits into from
Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6d8e71c
SCA table: Latest scans
chantal-kelm Sep 14, 2022
bf727a0
redirect and states
chantal-kelm Sep 21, 2022
7fba6a2
params url
chantal-kelm Sep 21, 2022
6bb26a4
the tables are working properly
chantal-kelm Sep 21, 2022
f9fa2fd
styles
chantal-kelm Sep 21, 2022
6574890
inventory test
chantal-kelm Sep 22, 2022
71f129d
styles euiFlexGroup
chantal-kelm Sep 22, 2022
9e03a69
SCA test, back button and parameter renaming
chantal-kelm Sep 23, 2022
ba53ea9
load for tables
chantal-kelm Sep 25, 2022
0089174
Added new policies table for reuse in agent welcome and sca
Machi3mfl Oct 17, 2022
e458ca6
Fixed table redirection on row clicked
Machi3mfl Oct 18, 2022
253912d
Solved review comments
Machi3mfl Oct 18, 2022
db0bdb0
Merge branch '4.4-7.10' into 4238-Redesign-the-agents-dashboard
Machi3mfl Oct 19, 2022
65a3d96
Fixed policy row redirect, renaming and remove it unused code
Machi3mfl Oct 19, 2022
d27f9f8
Updated CHANGELOG
Machi3mfl Oct 19, 2022
8bd7d74
Added pagination
Machi3mfl Oct 20, 2022
15e1e48
Merge branch '4.4-7.10' into 4238-Redesign-the-agents-dashboard
Machi3mfl Oct 20, 2022
59b6c63
Updated snapshots to fix unit tests
Machi3mfl Oct 20, 2022
f3b0873
Merge branch '4.4-7.10' into 4238-Redesign-the-agents-dashboard
Machi3mfl Oct 20, 2022
5b712e9
Updated CHANGELOG
Machi3mfl Oct 20, 2022
49f714f
Fixed lastest scan navigation
Machi3mfl Oct 20, 2022
3eb8834
Merge branch '4.4-7.10' into 4238-Redesign-the-agents-dashboard
Machi3mfl Oct 20, 2022
4e4896c
Merge branch '4.4-7.10' into 4238-Redesign-the-agents-dashboard
Machi3mfl Oct 21, 2022
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:/wazuh/wazuh-kibana-app/pull/4323)
- Added agent synchronization status in the agent module. [#3874](https:/wazuh/wazuh-kibana-app/pull/3874)
- Redesign the SCA table from agent's dashboard [#4512](https:/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>
Machi3mfl marked this conversation as resolved.
Show resolved Hide resolved
{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