diff --git a/opencti-platform/opencti-front/src/components/list_lines/ListLines.js b/opencti-platform/opencti-front/src/components/list_lines/ListLines.js index 51095ca0efea..7fdc53bbd50e 100644 --- a/opencti-platform/opencti-front/src/components/list_lines/ListLines.js +++ b/opencti-platform/opencti-front/src/components/list_lines/ListLines.js @@ -23,18 +23,18 @@ import StixDomainEntitiesExports from '../../private/components/common/stix_doma const styles = (theme) => ({ container: { - transition: theme.transitions.create('margin', { + transition: theme.transitions.create('padding', { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen, }), }, containerOpenExports: { flexGrow: 1, - transition: theme.transitions.create('margin', { + transition: theme.transitions.create('padding', { easing: theme.transitions.easing.easeOut, duration: theme.transitions.duration.enteringScreen, }), - marginRight: 310, + paddingRight: 310, }, parameters: { float: 'left', @@ -121,6 +121,7 @@ class ListLines extends Component { handleRemoveFilter, handleToggleExports, openExports, + noPadding, dataColumns, secondaryAction, paginationOptions, @@ -134,7 +135,9 @@ class ListLines extends Component { return (
@@ -285,6 +288,7 @@ ListLines.propTypes = { handleRemoveFilter: PropTypes.func, handleToggleExports: PropTypes.func, openExports: PropTypes.bool, + noPadding: PropTypes.bool, views: PropTypes.array, exportEntityType: PropTypes.string, keyword: PropTypes.string, diff --git a/opencti-platform/opencti-front/src/private/components/common/files/FileLine.js b/opencti-platform/opencti-front/src/private/components/common/files/FileLine.js index fabc4c59cc28..b29c3f4bcc19 100644 --- a/opencti-platform/opencti-front/src/private/components/common/files/FileLine.js +++ b/opencti-platform/opencti-front/src/private/components/common/files/FileLine.js @@ -114,7 +114,7 @@ class FileLineComponent extends Component { const { lastModifiedSinceMin, uploadStatus } = file; const isFail = uploadStatus === 'error' || uploadStatus === 'partial'; const isProgress = uploadStatus === 'progress'; - const isOutdated = isProgress && lastModifiedSinceMin > 5; + const isOutdated = isProgress && lastModifiedSinceMin > 1; const isImportActive = () => connectors && filter((x) => x.data.active, connectors).length > 0; const fileName = propOr('', 'name', file).includes('_') ? pipe(split('_'), drop(1), join('_'))(file.name) @@ -183,7 +183,6 @@ class FileLineComponent extends Component { diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_object/StixObjectTags.js b/opencti-platform/opencti-front/src/private/components/common/stix_object/StixObjectTags.js index ed181f85d0e7..b52269a57fcf 100644 --- a/opencti-platform/opencti-front/src/private/components/common/stix_object/StixObjectTags.js +++ b/opencti-platform/opencti-front/src/private/components/common/stix_object/StixObjectTags.js @@ -68,7 +68,7 @@ class StixObjectTags extends Component { tagEdge.node.id, tagEdge.node.value, ) - : '' + : null } /> ), @@ -82,7 +82,7 @@ class StixObjectTags extends Component { onClick={ typeof onClick === 'function' ? onClick.bind(this, 'tags', null, null) - : '' + : null } /> )} @@ -99,7 +99,4 @@ StixObjectTags.propTypes = { tags: PropTypes.object, }; -export default compose( - inject18n, - withStyles(styles), -)(StixObjectTags); +export default compose(inject18n, withStyles(styles))(StixObjectTags); diff --git a/opencti-platform/opencti-front/src/private/components/entities/cities/CityReports.js b/opencti-platform/opencti-front/src/private/components/entities/cities/CityReports.js index 4a022c5d40a0..9aa4b3a96cbc 100644 --- a/opencti-platform/opencti-front/src/private/components/entities/cities/CityReports.js +++ b/opencti-platform/opencti-front/src/private/components/entities/cities/CityReports.js @@ -10,9 +10,20 @@ import CityPopover from './CityPopover'; import Reports from '../../reports/Reports'; import StixDomainEntityHeader from '../../common/stix_domain_entities/StixDomainEntityHeader'; -const styles = () => ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -24,16 +35,30 @@ const styles = () => ({ }); class CityReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, city } = this.props; return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -56,7 +81,4 @@ const CityReports = createFragmentContainer(CityReportsComponent, { `, }); -export default compose( - inject18n, - withStyles(styles), -)(CityReports); +export default compose(inject18n, withStyles(styles))(CityReports); diff --git a/opencti-platform/opencti-front/src/private/components/entities/countries/CountryReports.js b/opencti-platform/opencti-front/src/private/components/entities/countries/CountryReports.js index c4f39a5823bb..e81d1e440b6e 100644 --- a/opencti-platform/opencti-front/src/private/components/entities/countries/CountryReports.js +++ b/opencti-platform/opencti-front/src/private/components/entities/countries/CountryReports.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import * as PropTypes from 'prop-types'; import { compose } from 'ramda'; import { createFragmentContainer } from 'react-relay'; import graphql from 'babel-plugin-relay/macro'; @@ -10,9 +10,20 @@ import CountryPopover from './CountryPopover'; import Reports from '../../reports/Reports'; import StixDomainEntityHeader from '../../common/stix_domain_entities/StixDomainEntityHeader'; -const styles = () => ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -24,16 +35,30 @@ const styles = () => ({ }); class CountryReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, country } = this.props; return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -56,7 +81,4 @@ const CountryReports = createFragmentContainer(CountryReportsComponent, { `, }); -export default compose( - inject18n, - withStyles(styles), -)(CountryReports); +export default compose(inject18n, withStyles(styles))(CountryReports); diff --git a/opencti-platform/opencti-front/src/private/components/entities/organizations/OrganizationReports.js b/opencti-platform/opencti-front/src/private/components/entities/organizations/OrganizationReports.js index c4ae55fa4ffc..c09485c69e1a 100644 --- a/opencti-platform/opencti-front/src/private/components/entities/organizations/OrganizationReports.js +++ b/opencti-platform/opencti-front/src/private/components/entities/organizations/OrganizationReports.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import * as PropTypes from 'prop-types'; import { compose, contains } from 'ramda'; import { createFragmentContainer } from 'react-relay'; import graphql from 'babel-plugin-relay/macro'; @@ -10,9 +10,20 @@ import OrganizationPopover from './OrganizationPopover'; import Reports from '../../reports/Reports'; import StixDomainEntityHeader from '../../common/stix_domain_entities/StixDomainEntityHeader'; -const styles = () => ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -26,31 +37,53 @@ const styles = () => ({ const organizationTypesForAuthorMode = ['vendor', 'csirt', 'partner']; class OrganizationReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, organization } = this.props; if ( contains(organization.organization_class, organizationTypesForAuthorMode) ) { return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); } return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -77,7 +110,4 @@ const OrganizationReports = createFragmentContainer( }, ); -export default compose( - inject18n, - withStyles(styles), -)(OrganizationReports); +export default compose(inject18n, withStyles(styles))(OrganizationReports); diff --git a/opencti-platform/opencti-front/src/private/components/entities/persons/PersonReports.js b/opencti-platform/opencti-front/src/private/components/entities/persons/PersonReports.js index 176b5d2e37c2..a23ea20389c2 100644 --- a/opencti-platform/opencti-front/src/private/components/entities/persons/PersonReports.js +++ b/opencti-platform/opencti-front/src/private/components/entities/persons/PersonReports.js @@ -10,9 +10,20 @@ import PersonPopover from './PersonPopover'; import Reports from '../../reports/Reports'; import StixDomainEntityHeader from '../../common/stix_domain_entities/StixDomainEntityHeader'; -const styles = () => ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -24,16 +35,30 @@ const styles = () => ({ }); class PersonReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, person } = this.props; return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -56,7 +81,4 @@ const PersonReports = createFragmentContainer(PersonReportsComponent, { `, }); -export default compose( - inject18n, - withStyles(styles), -)(PersonReports); +export default compose(inject18n, withStyles(styles))(PersonReports); diff --git a/opencti-platform/opencti-front/src/private/components/entities/regions/RegionReports.js b/opencti-platform/opencti-front/src/private/components/entities/regions/RegionReports.js index 1d249b61deca..29caddf7d24b 100644 --- a/opencti-platform/opencti-front/src/private/components/entities/regions/RegionReports.js +++ b/opencti-platform/opencti-front/src/private/components/entities/regions/RegionReports.js @@ -10,9 +10,20 @@ import RegionPopover from './RegionPopover'; import Reports from '../../reports/Reports'; import StixDomainEntityHeader from '../../common/stix_domain_entities/StixDomainEntityHeader'; -const styles = () => ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -24,16 +35,30 @@ const styles = () => ({ }); class RegionReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, region } = this.props; return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -56,7 +81,4 @@ const RegionReports = createFragmentContainer(RegionReportsComponent, { `, }); -export default compose( - inject18n, - withStyles(styles), -)(RegionReports); +export default compose(inject18n, withStyles(styles))(RegionReports); diff --git a/opencti-platform/opencti-front/src/private/components/entities/sectors/SectorReports.js b/opencti-platform/opencti-front/src/private/components/entities/sectors/SectorReports.js index 7afddd7865ce..8075d2ac0ae7 100644 --- a/opencti-platform/opencti-front/src/private/components/entities/sectors/SectorReports.js +++ b/opencti-platform/opencti-front/src/private/components/entities/sectors/SectorReports.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import * as PropTypes from 'prop-types'; import { compose } from 'ramda'; import { createFragmentContainer } from 'react-relay'; import graphql from 'babel-plugin-relay/macro'; @@ -10,9 +10,20 @@ import SectorPopover from './SectorPopover'; import Reports from '../../reports/Reports'; import StixDomainEntityHeader from '../../common/stix_domain_entities/StixDomainEntityHeader'; -const styles = () => ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -24,16 +35,30 @@ const styles = () => ({ }); class SectorReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, sector } = this.props; return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -56,7 +81,4 @@ const SectorReports = createFragmentContainer(SectorReportsComponent, { `, }); -export default compose( - inject18n, - withStyles(styles), -)(SectorReports); +export default compose(inject18n, withStyles(styles))(SectorReports); diff --git a/opencti-platform/opencti-front/src/private/components/reports/ReportEntities.js b/opencti-platform/opencti-front/src/private/components/reports/ReportEntities.js index 763fa29be41a..e83c144b18af 100644 --- a/opencti-platform/opencti-front/src/private/components/reports/ReportEntities.js +++ b/opencti-platform/opencti-front/src/private/components/reports/ReportEntities.js @@ -1,6 +1,8 @@ import React, { Component } from 'react'; import * as PropTypes from 'prop-types'; -import { compose, propOr } from 'ramda'; +import { + append, compose, filter, propOr, +} from 'ramda'; import graphql from 'babel-plugin-relay/macro'; import { createFragmentContainer } from 'react-relay'; import { QueryRenderer } from '../../../relay/environment'; @@ -28,6 +30,8 @@ class ReportEntitiesComponent extends Component { sortBy: propOr('name', 'sortBy', params), orderAsc: propOr(false, 'orderAsc', params), searchTerm: propOr('', 'searchTerm', params), + openExports: false, + numberOfElements: { number: 0, symbol: '' }, }; } @@ -48,9 +52,30 @@ class ReportEntitiesComponent extends Component { this.setState({ sortBy: field, orderAsc }, () => this.saveView()); } + handleToggleExports() { + this.setState({ openExports: !this.state.openExports }); + } + + handleToggle(type) { + if (this.state.types.includes(type)) { + this.setState({ types: filter((t) => t !== type, this.state.types) }, () => this.saveView()); + } else { + this.setState({ types: append(type, this.state.types) }, () => this.saveView()); + } + } + + setNumberOfElements(numberOfElements) { + this.setState({ numberOfElements }); + } + render() { const { report } = this.props; - const { sortBy, orderAsc, searchTerm } = this.state; + const { + sortBy, + orderAsc, + searchTerm, + numberOfElements, + } = this.state; const dataColumns = { entity_type: { label: 'Type', @@ -94,6 +119,7 @@ class ReportEntitiesComponent extends Component { handleSort={this.handleSort.bind(this)} handleSearch={this.handleSearch.bind(this)} secondaryAction={true} + numberOfElements={numberOfElements} > )} /> diff --git a/opencti-platform/opencti-front/src/private/components/reports/ReportEntitiesLines.js b/opencti-platform/opencti-front/src/private/components/reports/ReportEntitiesLines.js index ba41ee478cd6..c4e2119589cd 100644 --- a/opencti-platform/opencti-front/src/private/components/reports/ReportEntitiesLines.js +++ b/opencti-platform/opencti-front/src/private/components/reports/ReportEntitiesLines.js @@ -5,10 +5,21 @@ import graphql from 'babel-plugin-relay/macro'; import { pathOr, propOr } from 'ramda'; import ListLinesContent from '../../../components/list_lines/ListLinesContent'; import { ReportEntityLine, ReportEntityLineDummy } from './ReportEntityLine'; +import { setNumberOfElements } from '../../../utils/Number'; const nbOfRowsToLoad = 25; class ReportEntitiesLines extends Component { + componentDidUpdate(prevProps) { + setNumberOfElements( + prevProps, + this.props, + 'objectRefs', + this.props.setNumberOfElements.bind(this), + 'report', + ); + } + render() { const { initialLoading, @@ -49,6 +60,7 @@ ReportEntitiesLines.propTypes = { relay: PropTypes.object, initialLoading: PropTypes.bool, searchTerm: PropTypes.string, + setNumberOfElements: PropTypes.func, }; export const ReportEntitiesLinesQuery = graphql` @@ -59,7 +71,7 @@ export const ReportEntitiesLinesQuery = graphql` $cursor: ID $orderBy: StixDomainEntitiesOrdering $orderMode: OrderingMode - $filters: [ StixDomainEntitiesFiltering] + $filters: [StixDomainEntitiesFiltering] ) { report(id: $id) { ...ReportEntitiesLines_report diff --git a/opencti-platform/opencti-front/src/private/components/reports/ReportObservables.js b/opencti-platform/opencti-front/src/private/components/reports/ReportObservables.js index 4d5f05bc6dd0..4a513be5e45b 100644 --- a/opencti-platform/opencti-front/src/private/components/reports/ReportObservables.js +++ b/opencti-platform/opencti-front/src/private/components/reports/ReportObservables.js @@ -1,8 +1,11 @@ import React, { Component } from 'react'; import * as PropTypes from 'prop-types'; -import { compose, propOr } from 'ramda'; +import { + append, compose, filter, propOr, +} from 'ramda'; import graphql from 'babel-plugin-relay/macro'; import { createFragmentContainer } from 'react-relay'; +import { withStyles } from '@material-ui/core'; import { QueryRenderer } from '../../../relay/environment'; import ReportHeader from './ReportHeader'; import ListLines from '../../../components/list_lines/ListLines'; @@ -15,6 +18,14 @@ import { } from '../../../utils/ListParameters'; import inject18n from '../../../components/i18n'; import ReportAddObservableRefs from './ReportAddObservableRefs'; +import StixObservablesRightBar from '../signatures/stix_observables/StixObservablesRightBar'; + +const styles = () => ({ + container: { + margin: 0, + padding: '0 250px 0 0', + }, +}); class ReportObservablesComponent extends Component { constructor(props) { @@ -28,6 +39,9 @@ class ReportObservablesComponent extends Component { sortBy: propOr('created_at', 'sortBy', params), orderAsc: propOr(false, 'orderAsc', params), searchTerm: propOr('', 'searchTerm', params), + types: [], + openExports: false, + numberOfElements: { number: 0, symbol: '' }, }; } @@ -48,9 +62,32 @@ class ReportObservablesComponent extends Component { this.setState({ sortBy: field, orderAsc }, () => this.saveView()); } + handleToggleExports() { + this.setState({ openExports: !this.state.openExports }); + } + + handleToggle(type) { + if (this.state.types.includes(type)) { + this.setState({ types: filter((t) => t !== type, this.state.types) }, () => this.saveView()); + } else { + this.setState({ types: append(type, this.state.types) }, () => this.saveView()); + } + } + + setNumberOfElements(numberOfElements) { + this.setState({ numberOfElements }); + } + render() { - const { report } = this.props; - const { sortBy, orderAsc, searchTerm } = this.state; + const { report, classes } = this.props; + const { + sortBy, + orderAsc, + searchTerm, + types, + openExports, + numberOfElements, + } = this.state; const dataColumns = { entity_type: { label: 'Type', @@ -78,13 +115,13 @@ class ReportObservablesComponent extends Component { }, }; const paginationOptions = { - types: null, + types, search: searchTerm, orderBy: sortBy, orderMode: orderAsc ? 'asc' : 'desc', }; return ( -
+

)} /> @@ -112,6 +151,11 @@ class ReportObservablesComponent extends Component { reportId={report.id} paginationOptions={paginationOptions} /> +
); } @@ -134,4 +178,4 @@ const ReportObservables = createFragmentContainer(ReportObservablesComponent, { `, }); -export default compose(inject18n)(ReportObservables); +export default compose(inject18n, withStyles(styles))(ReportObservables); diff --git a/opencti-platform/opencti-front/src/private/components/reports/ReportObservablesLines.js b/opencti-platform/opencti-front/src/private/components/reports/ReportObservablesLines.js index de31b8de52c3..f02e71511b06 100644 --- a/opencti-platform/opencti-front/src/private/components/reports/ReportObservablesLines.js +++ b/opencti-platform/opencti-front/src/private/components/reports/ReportObservablesLines.js @@ -8,10 +8,21 @@ import { ReportObservableLine, ReportObservableLineDummy, } from './ReportObservableLine'; +import { setNumberOfElements } from '../../../utils/Number'; const nbOfRowsToLoad = 25; class ReportObservablesLines extends Component { + componentDidUpdate(prevProps) { + setNumberOfElements( + prevProps, + this.props, + 'observableRefs', + this.props.setNumberOfElements.bind(this), + 'report', + ); + } + render() { const { initialLoading, @@ -52,11 +63,13 @@ ReportObservablesLines.propTypes = { relay: PropTypes.object, initialLoading: PropTypes.bool, searchTerm: PropTypes.string, + setNumberOfElements: PropTypes.func, }; export const reportObservablesLinesQuery = graphql` query ReportObservablesLinesQuery( $id: String! + $types: [String] $search: String $count: Int! $cursor: ID @@ -67,6 +80,7 @@ export const reportObservablesLinesQuery = graphql` report(id: $id) { ...ReportObservablesLines_report @arguments( + types: $types search: $search count: $count cursor: $cursor @@ -84,6 +98,7 @@ export default createPaginationContainer( report: graphql` fragment ReportObservablesLines_report on Report @argumentDefinitions( + types: { type: "[String]" } search: { type: "String" } count: { type: "Int", defaultValue: 25 } cursor: { type: "ID" } @@ -93,6 +108,7 @@ export default createPaginationContainer( ) { id observableRefs( + types: $types search: $search first: $count after: $cursor @@ -130,6 +146,7 @@ export default createPaginationContainer( id: fragmentVariables.id, count, cursor, + types: fragmentVariables.types, search: fragmentVariables.search, orderBy: fragmentVariables.orderBy, orderMode: fragmentVariables.orderMode, diff --git a/opencti-platform/opencti-front/src/private/components/reports/Reports.js b/opencti-platform/opencti-front/src/private/components/reports/Reports.js index e3d63fd768a4..694cbae5b0ba 100644 --- a/opencti-platform/opencti-front/src/private/components/reports/Reports.js +++ b/opencti-platform/opencti-front/src/private/components/reports/Reports.js @@ -28,7 +28,7 @@ class Reports extends Component { const params = buildViewParamsFromUrlAndStorage( props.history, props.location, - `view-reports${this.props.objectId ? `-${this.props.objectId}` : ''}`, + `view-reports${props.objectId ? `-${props.objectId}` : ''}`, ); this.state = { sortBy: propOr('published', 'sortBy', params), @@ -59,7 +59,11 @@ class Reports extends Component { } handleToggleExports() { - this.setState({ openExports: !this.state.openExports }); + this.setState({ openExports: !this.state.openExports }, () => { + if (typeof this.props.onChangeOpenExports === 'function') { + this.props.onChangeOpenExports(this.state.openExports); + } + }); } handleAddFilter(key, id, value, event) { @@ -129,6 +133,7 @@ class Reports extends Component { handleRemoveFilter={this.handleRemoveFilter.bind(this)} handleToggleExports={this.handleToggleExports.bind(this)} openExports={openExports} + noPadding={typeof this.props.onChangeOpenExports === 'function'} exportEntityType="Report" keyword={searchTerm} filters={filters} @@ -211,6 +216,7 @@ Reports.propTypes = { history: PropTypes.object, location: PropTypes.object, displayCreate: PropTypes.bool, + onChangeOpenExports: PropTypes.func, }; export default compose(inject18n, withRouter)(Reports); diff --git a/opencti-platform/opencti-front/src/private/components/signatures/StixObservables.js b/opencti-platform/opencti-front/src/private/components/signatures/StixObservables.js index c72391d142af..c404e84bc634 100644 --- a/opencti-platform/opencti-front/src/private/components/signatures/StixObservables.js +++ b/opencti-platform/opencti-front/src/private/components/signatures/StixObservables.js @@ -31,7 +31,7 @@ import { const styles = () => ({ container: { margin: 0, - padding: '0 260px 0 0', + padding: '0 250px 0 0', }, }); @@ -50,6 +50,7 @@ class StixObservables extends Component { view: propOr('lines', 'view', params), filters: {}, types: [], + openExports: false, numberOfElements: { number: 0, symbol: '' }, }; } diff --git a/opencti-platform/opencti-front/src/private/components/signatures/indicators/EntityIndicatorLine.js b/opencti-platform/opencti-front/src/private/components/signatures/indicators/EntityIndicatorLine.js index 91e48c025df6..54be07f037c8 100644 --- a/opencti-platform/opencti-front/src/private/components/signatures/indicators/EntityIndicatorLine.js +++ b/opencti-platform/opencti-front/src/private/components/signatures/indicators/EntityIndicatorLine.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import * as PropTypes from 'prop-types'; -import { compose } from 'ramda'; +import { compose, pathOr, take } from 'ramda'; import { Link } from 'react-router-dom'; import { createFragmentContainer } from 'react-relay'; import graphql from 'babel-plugin-relay/macro'; @@ -12,9 +12,10 @@ import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; import { MoreVert } from '@material-ui/icons'; import { ShieldSearch } from 'mdi-material-ui'; import inject18n from '../../../../components/i18n'; -import ItemConfidenceLevel from '../../../../components/ItemConfidenceLevel'; import StixRelationPopover from '../../common/stix_relations/StixRelationPopover'; import ItemPatternType from '../../../../components/ItemPatternType'; +import StixObjectTags from '../../common/stix_object/StixObjectTags'; +import ItemMarking from '../../../../components/ItemMarking'; const styles = (theme) => ({ item: { @@ -69,7 +70,7 @@ class EntityIndicatorLineComponent extends Component {
{node.to.name}
- {node.inferred ? '-' : node.role_played} +
- {node.inferred ? '-' : nsd(node.first_seen)} + {nsd(node.to.valid_from)}
- {node.inferred ? '-' : nsd(node.last_seen)} + {nsd(node.to.valid_until)}
- + {take( + 1, + pathOr([], ['markingDefinitions', 'edges'], node.to), + ).map((markingDefinition) => ( + + ))}
} @@ -157,6 +164,27 @@ const EntityIndicatorLineFragment = createFragmentContainer( valid_until score created + markingDefinitions { + edges { + node { + id + definition + } + } + } + tags { + edges { + node { + id + tag_type + value + color + } + relation { + id + } + } + } } } } @@ -182,37 +210,37 @@ class EntityIndicatorLineDummyComponent extends Component {
diff --git a/opencti-platform/opencti-front/src/private/components/signatures/indicators/EntityIndicators.js b/opencti-platform/opencti-front/src/private/components/signatures/indicators/EntityIndicators.js index 78e4c3c5a947..774015091b8e 100644 --- a/opencti-platform/opencti-front/src/private/components/signatures/indicators/EntityIndicators.js +++ b/opencti-platform/opencti-front/src/private/components/signatures/indicators/EntityIndicators.js @@ -1,95 +1,132 @@ import React, { Component } from 'react'; import * as PropTypes from 'prop-types'; -import { compose } from 'ramda'; -import Drawer from '@material-ui/core/Drawer'; -import Grid from '@material-ui/core/Grid'; -import FormControlLabel from '@material-ui/core/FormControlLabel'; -import Switch from '@material-ui/core/Switch'; -import { withStyles } from '@material-ui/core'; +import { propOr } from 'ramda'; import EntityIndicatorsLines, { entityIndicatorsLinesQuery, } from './EntityIndicatorsLines'; import ListLines from '../../../../components/list_lines/ListLines'; -import inject18n from '../../../../components/i18n'; import { QueryRenderer } from '../../../../relay/environment'; import StixRelationCreationFromEntity from '../../common/stix_relations/StixRelationCreationFromEntity'; - -const styles = (theme) => ({ - bottomNav: { - zIndex: 1000, - padding: '10px 274px 10px 84px', - backgroundColor: theme.palette.navBottom.background, - display: 'flex', - }, -}); +import { + buildViewParamsFromUrlAndStorage, + saveViewParameters, +} from '../../../../utils/ListParameters'; class EntityIndicators extends Component { constructor(props) { super(props); + const params = buildViewParamsFromUrlAndStorage( + props.history, + props.location, + `view-indicators-${props.entityId}`, + ); this.state = { - sortBy: 'first_seen', - orderAsc: false, + sortBy: propOr('first_seen', 'sortBy', params), + orderAsc: propOr(false, 'orderAsc', params), + searchTerm: propOr('', 'searchTerm', params), + view: 'lines', lastSeenStart: null, lastSeenStop: null, targetEntityTypes: ['Indicator'], - view: 'lines', inferred: false, }; } - handleChangeInferred() { - this.setState({ - inferred: !this.state.inferred, - sortBy: !this.state.inferred ? null : this.state.sortBy, - }); + saveView() { + saveViewParameters( + this.props.history, + this.props.location, + `view-indicators-${this.props.entityId}`, + this.state, + ); + } + + handleSearch(value) { + this.setState({ searchTerm: value }, () => this.saveView()); } handleSort(field, orderAsc) { - this.setState({ sortBy: field, orderAsc }); + this.setState({ sortBy: field, orderAsc }, () => this.saveView()); + } + + handleToggleExports() { + this.setState({ openExports: !this.state.openExports }, () => { + if (typeof this.props.onChangeOpenExports === 'function') { + this.props.onChangeOpenExports(this.state.openExports); + } + }); + } + + setNumberOfElements(numberOfElements) { + this.setState({ numberOfElements }); } renderLines(paginationOptions) { - const { sortBy, orderAsc } = this.state; - const { entityLink } = this.props; + const { + sortBy, + orderAsc, + searchTerm, + openExports, + numberOfElements, + } = this.state; + const { entityId, entityLink } = this.props; const dataColumns = { - pattern_type: { + toPatternType: { label: 'Type', width: '10%', - isSortable: false, + isSortable: true, }, - name: { + toName: { label: 'Name', width: '30%', - isSortable: false, + isSortable: true, }, - role_played: { - label: 'Played role', + tags: { + label: 'Tags', width: '15%', isSortable: false, }, - first_seen: { - label: 'First obs.', + toValidFrom: { + label: 'Valid from', width: '15%', isSortable: true, }, - last_seen: { - label: 'Last obs.', + toValidUntil: { + label: 'Valid until', width: '15%', isSortable: true, }, - weight: { - label: 'Confidence level', - isSortable: true, + markingDefinitions: { + label: 'Marking', + isSortable: false, }, }; + const orderByMapping = { + toPatternType: 'pattern_type', + toName: 'name', + toValidFrom: 'valid_from', + toValidUntil: 'valid_until', + }; + const exportPaginationOptions = { + filters: [{ key: 'indicates', values: [entityId] }], + orderBy: orderByMapping[sortBy === 'first_seen' ? 'toValidFrom' : sortBy], + orderMode: orderAsc ? 'asc' : 'desc', + search: searchTerm, + }; return ( )} /> @@ -109,9 +147,7 @@ class EntityIndicators extends Component { } render() { - const { - t, entityId, relationType, classes, - } = this.props; + const { entityId, relationType } = this.props; const { view, targetEntityTypes, @@ -120,9 +156,11 @@ class EntityIndicators extends Component { lastSeenStart, lastSeenStop, inferred, + searchTerm, } = this.state; const paginationOptions = { inferred, + search: searchTerm, toTypes: targetEntityTypes, fromId: entityId, relationType, @@ -133,27 +171,6 @@ class EntityIndicators extends Component { }; return (
- - - - - } - label={t('Inferences')} - /> - - - {view === 'lines' ? this.renderLines(paginationOptions) : ''} ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -24,16 +35,30 @@ const styles = () => ({ }); class AttackPatternReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, attackPattern } = this.props; return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -59,7 +84,4 @@ const AttackPatternReports = createFragmentContainer( }, ); -export default compose( - inject18n, - withStyles(styles), -)(AttackPatternReports); +export default compose(inject18n, withStyles(styles))(AttackPatternReports); diff --git a/opencti-platform/opencti-front/src/private/components/techniques/tools/ToolReports.js b/opencti-platform/opencti-front/src/private/components/techniques/tools/ToolReports.js index 949a83c053c1..26a360fe19ad 100644 --- a/opencti-platform/opencti-front/src/private/components/techniques/tools/ToolReports.js +++ b/opencti-platform/opencti-front/src/private/components/techniques/tools/ToolReports.js @@ -10,9 +10,20 @@ import ToolPopover from './ToolPopover'; import Reports from '../../reports/Reports'; import StixDomainEntityHeader from '../../common/stix_domain_entities/StixDomainEntityHeader'; -const styles = () => ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -24,16 +35,30 @@ const styles = () => ({ }); class ToolReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, tool } = this.props; return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -56,7 +81,4 @@ const ToolReports = createFragmentContainer(ToolReportsComponent, { `, }); -export default compose( - inject18n, - withStyles(styles), -)(ToolReports); +export default compose(inject18n, withStyles(styles))(ToolReports); diff --git a/opencti-platform/opencti-front/src/private/components/techniques/vulnerabilities/VulnerabilityReports.js b/opencti-platform/opencti-front/src/private/components/techniques/vulnerabilities/VulnerabilityReports.js index b9068e2ba5ad..ff5fba55b797 100644 --- a/opencti-platform/opencti-front/src/private/components/techniques/vulnerabilities/VulnerabilityReports.js +++ b/opencti-platform/opencti-front/src/private/components/techniques/vulnerabilities/VulnerabilityReports.js @@ -10,9 +10,20 @@ import VulnerabilityPopover from './VulnerabilityPopover'; import Reports from '../../reports/Reports'; import StixDomainEntityHeader from '../../common/stix_domain_entities/StixDomainEntityHeader'; -const styles = () => ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -24,16 +35,30 @@ const styles = () => ({ }); class VulnerabilityReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, vulnerability } = this.props; return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -59,7 +84,4 @@ const VulnerabilityReports = createFragmentContainer( }, ); -export default compose( - inject18n, - withStyles(styles), -)(VulnerabilityReports); +export default compose(inject18n, withStyles(styles))(VulnerabilityReports); diff --git a/opencti-platform/opencti-front/src/private/components/threats/campaigns/CampaignReports.js b/opencti-platform/opencti-front/src/private/components/threats/campaigns/CampaignReports.js index 48f1fd4d3200..937d34e48ea8 100644 --- a/opencti-platform/opencti-front/src/private/components/threats/campaigns/CampaignReports.js +++ b/opencti-platform/opencti-front/src/private/components/threats/campaigns/CampaignReports.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import * as PropTypes from 'prop-types'; import { compose } from 'ramda'; import { createFragmentContainer } from 'react-relay'; import graphql from 'babel-plugin-relay/macro'; @@ -10,9 +10,20 @@ import CampaignPopover from './CampaignPopover'; import Reports from '../../reports/Reports'; import StixDomainEntityHeader from '../../common/stix_domain_entities/StixDomainEntityHeader'; -const styles = () => ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -24,16 +35,30 @@ const styles = () => ({ }); class CampaignReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, campaign } = this.props; return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -56,7 +81,4 @@ const CampaignReports = createFragmentContainer(CampaignReportsComponent, { `, }); -export default compose( - inject18n, - withStyles(styles), -)(CampaignReports); +export default compose(inject18n, withStyles(styles))(CampaignReports); diff --git a/opencti-platform/opencti-front/src/private/components/threats/incidents/IncidentReports.js b/opencti-platform/opencti-front/src/private/components/threats/incidents/IncidentReports.js index 9762bc0e0706..f1e5795669ae 100644 --- a/opencti-platform/opencti-front/src/private/components/threats/incidents/IncidentReports.js +++ b/opencti-platform/opencti-front/src/private/components/threats/incidents/IncidentReports.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import * as PropTypes from 'prop-types'; import { compose } from 'ramda'; import { createFragmentContainer } from 'react-relay'; import graphql from 'babel-plugin-relay/macro'; @@ -10,9 +10,20 @@ import IncidentPopover from './IncidentPopover'; import Reports from '../../reports/Reports'; import StixDomainEntityHeader from '../../common/stix_domain_entities/StixDomainEntityHeader'; -const styles = () => ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -24,16 +35,30 @@ const styles = () => ({ }); class IncidentReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, incident } = this.props; return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -56,7 +81,4 @@ const IncidentReports = createFragmentContainer(IncidentReportsComponent, { `, }); -export default compose( - inject18n, - withStyles(styles), -)(IncidentReports); +export default compose(inject18n, withStyles(styles))(IncidentReports); diff --git a/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/IntrusionSetIndicators.js b/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/IntrusionSetIndicators.js index d2d887b8a8a8..063548201b67 100644 --- a/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/IntrusionSetIndicators.js +++ b/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/IntrusionSetIndicators.js @@ -22,8 +22,8 @@ const styles = () => ({ paper: { height: '100%', minHeight: '100%', - margin: '5px 0 40px 0', - padding: '15px', + margin: '5px 0 0 0', + padding: '25px 15px 15px 15px', borderRadius: 6, }, }); diff --git a/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/IntrusionSetReports.js b/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/IntrusionSetReports.js index 3b207cf8d677..d2ce9b265f26 100644 --- a/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/IntrusionSetReports.js +++ b/opencti-platform/opencti-front/src/private/components/threats/intrusion_sets/IntrusionSetReports.js @@ -10,9 +10,20 @@ import Reports from '../../reports/Reports'; import IntrusionSetPopover from './IntrusionSetPopover'; import StixDomainEntityHeader from '../../common/stix_domain_entities/StixDomainEntityHeader'; -const styles = () => ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -24,16 +35,30 @@ const styles = () => ({ }); class IntrusionSetReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, intrusionSet } = this.props; return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -59,7 +84,4 @@ const IntrusionSetReports = createFragmentContainer( }, ); -export default compose( - inject18n, - withStyles(styles), -)(IntrusionSetReports); +export default compose(inject18n, withStyles(styles))(IntrusionSetReports); diff --git a/opencti-platform/opencti-front/src/private/components/threats/malwares/MalwareReports.js b/opencti-platform/opencti-front/src/private/components/threats/malwares/MalwareReports.js index a6d202a83fd5..d18fa4e31e14 100644 --- a/opencti-platform/opencti-front/src/private/components/threats/malwares/MalwareReports.js +++ b/opencti-platform/opencti-front/src/private/components/threats/malwares/MalwareReports.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import * as PropTypes from 'prop-types'; import { compose } from 'ramda'; import { createFragmentContainer } from 'react-relay'; import graphql from 'babel-plugin-relay/macro'; @@ -10,9 +10,20 @@ import MalwarePopover from './MalwarePopover'; import Reports from '../../reports/Reports'; import StixDomainEntityHeader from '../../common/stix_domain_entities/StixDomainEntityHeader'; -const styles = () => ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -24,16 +35,30 @@ const styles = () => ({ }); class MalwareReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, malware } = this.props; return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -56,7 +81,4 @@ const MalwareReports = createFragmentContainer(MalwareReportsComponent, { `, }); -export default compose( - inject18n, - withStyles(styles), -)(MalwareReports); +export default compose(inject18n, withStyles(styles))(MalwareReports); diff --git a/opencti-platform/opencti-front/src/private/components/threats/threat_actors/ThreatActorReports.js b/opencti-platform/opencti-front/src/private/components/threats/threat_actors/ThreatActorReports.js index d94157573c28..b8cc23d3fb74 100644 --- a/opencti-platform/opencti-front/src/private/components/threats/threat_actors/ThreatActorReports.js +++ b/opencti-platform/opencti-front/src/private/components/threats/threat_actors/ThreatActorReports.js @@ -10,9 +10,20 @@ import ThreatActorPopover from './ThreatActorPopover'; import Reports from '../../reports/Reports'; import StixDomainEntityHeader from '../../common/stix_domain_entities/StixDomainEntityHeader'; -const styles = () => ({ +const styles = (theme) => ({ container: { - margin: 0, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + containerWithPadding: { + flexGrow: 1, + transition: theme.transitions.create('padding', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + paddingRight: 310, }, paper: { height: '100%', @@ -24,16 +35,30 @@ const styles = () => ({ }); class ThreatActorReportsComponent extends Component { + constructor(props) { + super(props); + this.state = { withPadding: false }; + } + render() { + const { withPadding } = this.state; const { classes, threatActor } = this.props; return ( -
+
} /> - + this.setState({ withPadding: openExports }) + } + />
); @@ -59,7 +84,4 @@ const ThreatActorReports = createFragmentContainer( }, ); -export default compose( - inject18n, - withStyles(styles), -)(ThreatActorReports); +export default compose(inject18n, withStyles(styles))(ThreatActorReports); diff --git a/opencti-platform/opencti-front/src/utils/ListParameters.js b/opencti-platform/opencti-front/src/utils/ListParameters.js index 92ef179ef174..d287570d8075 100644 --- a/opencti-platform/opencti-front/src/utils/ListParameters.js +++ b/opencti-platform/opencti-front/src/utils/ListParameters.js @@ -14,6 +14,10 @@ export const saveViewParameters = ( dissoc('observableTypes'), dissoc('openExports'), dissoc('numberOfElements'), + dissoc('lastSeenStart'), + dissoc('lastSeenStop'), + dissoc('targetEntityTypes'), + dissoc('inferred'), )(params); history.replace( `${location.pathname}?${new URLSearchParams(urlParams).toString()}`, diff --git a/opencti-platform/opencti-front/src/utils/Localization.js b/opencti-platform/opencti-front/src/utils/Localization.js index 7da928a03e1f..48643052402a 100644 --- a/opencti-platform/opencti-front/src/utils/Localization.js +++ b/opencti-platform/opencti-front/src/utils/Localization.js @@ -438,6 +438,7 @@ const i18n = { Month: 'Mois', Year: 'Année', Role: 'Rôle', + Date: 'Date', 'Start date': 'Date de début', 'End date': 'Date de fin', // Properties diff --git a/opencti-platform/opencti-front/src/utils/Number.js b/opencti-platform/opencti-front/src/utils/Number.js index 913edad29032..9d25a7276a6a 100644 --- a/opencti-platform/opencti-front/src/utils/Number.js +++ b/opencti-platform/opencti-front/src/utils/Number.js @@ -25,9 +25,9 @@ export const numberFormat = (number, digits = 2) => { }; }; -export const setNumberOfElements = (prevProps, props, key, callback) => { - const currentNumberOfElements = pathOr(0, [key, 'pageInfo', 'globalCount'], props.data); - const prevNumberOfElements = pathOr(0, [key, 'pageInfo', 'globalCount'], prevProps.data); +export const setNumberOfElements = (prevProps, props, key, callback, propKey = 'data') => { + const currentNumberOfElements = pathOr(0, [key, 'pageInfo', 'globalCount'], props[propKey]); + const prevNumberOfElements = pathOr(0, [key, 'pageInfo', 'globalCount'], prevProps[propKey]); if (currentNumberOfElements !== prevNumberOfElements) { callback(numberFormat(currentNumberOfElements)); } diff --git a/opencti-platform/opencti-graphql/config/schema/opencti.graphql b/opencti-platform/opencti-graphql/config/schema/opencti.graphql index df4cb8643936..3e8e853385d6 100644 --- a/opencti-platform/opencti-graphql/config/schema/opencti.graphql +++ b/opencti-platform/opencti-graphql/config/schema/opencti.graphql @@ -417,6 +417,11 @@ enum StixDomainEntitiesFilter { alias stix_id_key hasExternalReference + knowledgeContains + observablesContains + tags + createdBy + indicates } input StixDomainEntitiesFiltering { key: StixDomainEntitiesFilter! @@ -566,6 +571,11 @@ enum StixRelationsOrdering { modified created_at updated_at + toName + toValidFrom + toValidUntil + toPatternType + toCreatedAt } type StixRelationConnection { pageInfo: PageInfo! diff --git a/opencti-platform/opencti-graphql/src/database/grakn.js b/opencti-platform/opencti-graphql/src/database/grakn.js index bd6da802db1e..4569f0e478e2 100644 --- a/opencti-platform/opencti-graphql/src/database/grakn.js +++ b/opencti-platform/opencti-graphql/src/database/grakn.js @@ -68,6 +68,7 @@ const dateFormat = 'YYYY-MM-DDTHH:mm:ss'; const GraknString = 'String'; const GraknDate = 'Date'; +export const REL_CONNECTED_SUFFIX = 'CONNECTED'; export const TYPE_OPENCTI_INTERNAL = 'Internal'; export const TYPE_STIX_DOMAIN = 'Stix-Domain'; export const TYPE_STIX_DOMAIN_ENTITY = 'Stix-Domain-Entity'; @@ -819,9 +820,14 @@ export const listRelations = async (relationType, relationFilter, args) => { if (isRelationOrderBy) { const [relation, field] = orderBy.split('.'); const curatedRelation = relation.replace(REL_INDEX_PREFIX, ''); - relationsFields.push( - `($rel, $${curatedRelation}) isa ${curatedRelation}; $${curatedRelation} has ${field} $order;` - ); + if (curatedRelation.includes(REL_CONNECTED_SUFFIX)) { + const finalCuratedRelation = curatedRelation.replace(REL_CONNECTED_SUFFIX, ''); + relationsFields.push(`$${finalCuratedRelation} has ${field} $order;`); + } else { + relationsFields.push( + `($rel, $${curatedRelation}) isa ${curatedRelation}; $${curatedRelation} has ${field} $order;` + ); + } } else if (orderBy) { attributesFields.push(`$rel has ${orderBy} $order;`); } diff --git a/opencti-platform/opencti-graphql/src/domain/report.js b/opencti-platform/opencti-graphql/src/domain/report.js index eb7f076883c0..b3e031889a39 100644 --- a/opencti-platform/opencti-graphql/src/domain/report.js +++ b/opencti-platform/opencti-graphql/src/domain/report.js @@ -76,7 +76,7 @@ export const observableRefs = (reportId, args) => { } return findWithConnectedRelations( `match $from isa Report; $rel(observables_aggregation:$from, soo:$to) isa observable_refs; - $to isa ${args.type ? escape(args.type) : 'Stix-Domain-Entity'}; + $to isa ${args.type ? escape(args.type) : 'Stix-Observable'}; $from has internal_id_key "${escapeString(reportId)}"; get;`, 'to', 'rel' diff --git a/opencti-platform/opencti-graphql/src/domain/stixDomainEntity.js b/opencti-platform/opencti-graphql/src/domain/stixDomainEntity.js index fc5d1a146b19..a6222be636ea 100644 --- a/opencti-platform/opencti-graphql/src/domain/stixDomainEntity.js +++ b/opencti-platform/opencti-graphql/src/domain/stixDomainEntity.js @@ -1,4 +1,4 @@ -import { assoc, dissoc, map } from 'ramda'; +import { assoc, dissoc, map, propOr, pipe, invertObj } from 'ramda'; import { BUS_TOPICS } from '../config/conf'; import { delEditContext, notify, setEditContext } from '../database/redis'; import { @@ -22,6 +22,7 @@ import { generateFileExportName, upload } from '../database/minio'; import { connectorsForExport } from './connector'; import { createWork, workToExportFile } from './work'; import { pushToConnector } from '../database/rabbitmq'; +import stixDomainEntityResolvers from '../resolvers/stixDomainEntity'; export const findAll = args => { const noTypes = !args.types || args.types.length === 0; @@ -86,6 +87,29 @@ const askJobExports = async ( })); }, connectors) ); + let finalListArgs = listArgs; + if (listArgs !== null) { + const stixDomainEntitiesFiltersInversed = invertObj(stixDomainEntityResolvers.StixDomainEntitiesFilter); + const stixDomainEntitiesOrderingInversed = invertObj(stixDomainEntityResolvers.StixDomainEntitiesOrdering); + finalListArgs = pipe( + assoc( + 'filters', + map( + n => ({ + key: n.key in stixDomainEntitiesFiltersInversed ? stixDomainEntitiesFiltersInversed[n.key] : n.key, + values: n.values + }), + propOr([], 'filters', listArgs) + ) + ), + assoc( + 'orderBy', + listArgs.orderBy in stixDomainEntitiesOrderingInversed + ? stixDomainEntitiesOrderingInversed[listArgs.orderBy] + : listArgs.orderBy + ) + )(listArgs); + } // Send message to all correct connectors queues await Promise.all( map(data => { @@ -97,7 +121,7 @@ const askJobExports = async ( export_type: exportType, // for entity, simple or full / for list, withArgs / withoutArgs entity_type: entity ? entity.entity_type : type, // report, threat, ... entity_id: entity ? entity.id : null, // report(id), thread(id), ... - list_args: listArgs, + list_args: finalListArgs, file_name: work.work_file // Base path for the upload }; return pushToConnector(connector, message); diff --git a/opencti-platform/opencti-graphql/src/resolvers/file.js b/opencti-platform/opencti-graphql/src/resolvers/file.js index a6a0a1da6970..8d5905ad472d 100644 --- a/opencti-platform/opencti-graphql/src/resolvers/file.js +++ b/opencti-platform/opencti-graphql/src/resolvers/file.js @@ -4,8 +4,7 @@ import { loadFileWorks } from '../domain/work'; const fileResolvers = { Query: { - importFiles: (entity, { first }) => filesListing(first, 'import'), - + importFiles: (entity, { first }) => filesListing(first, 'import') }, File: { works: file => loadFileWorks(file.id) diff --git a/opencti-platform/opencti-graphql/src/resolvers/stixDomainEntity.js b/opencti-platform/opencti-graphql/src/resolvers/stixDomainEntity.js index d179e688a3f6..aef20a21f7d2 100644 --- a/opencti-platform/opencti-graphql/src/resolvers/stixDomainEntity.js +++ b/opencti-platform/opencti-graphql/src/resolvers/stixDomainEntity.js @@ -36,7 +36,12 @@ const stixDomainEntityResolvers = { tags: `${REL_INDEX_PREFIX}tagged.value` }, StixDomainEntitiesFilter: { - hasExternalReference: `${REL_INDEX_PREFIX}external_references.internal_id_key` + tags: `${REL_INDEX_PREFIX}tagged.internal_id_key`, + createdBy: `${REL_INDEX_PREFIX}created_by_ref.internal_id_key`, + knowledgeContains: `${REL_INDEX_PREFIX}object_refs.internal_id_key`, + observablesContains: `${REL_INDEX_PREFIX}observable_refs.internal_id_key`, + hasExternalReference: `${REL_INDEX_PREFIX}external_references.internal_id_key`, + indicates: `${REL_INDEX_PREFIX}indicates.internal_id_key` }, StixDomainEntity: { // eslint-disable-next-line no-underscore-dangle @@ -64,7 +69,8 @@ const stixDomainEntityResolvers = { }), stixDomainEntityAdd: (_, { input }, { user }) => addStixDomainEntity(user, input), stixDomainEntitiesExportAsk: (_, args) => stixDomainEntityExportAsk(args), - stixDomainEntitiesExportPush: (_, { type, file, listArgs }, { user }) => stixDomainEntityExportPush(user, type, null, file, listArgs) + stixDomainEntitiesExportPush: (_, { type, file, listArgs }, { user }) => + stixDomainEntityExportPush(user, type, null, file, listArgs) }, Subscription: { stixDomainEntity: { diff --git a/opencti-platform/opencti-graphql/src/resolvers/stixRelation.js b/opencti-platform/opencti-graphql/src/resolvers/stixRelation.js index 3113bff76246..8a0d1ed83b96 100644 --- a/opencti-platform/opencti-graphql/src/resolvers/stixRelation.js +++ b/opencti-platform/opencti-graphql/src/resolvers/stixRelation.js @@ -15,7 +15,8 @@ import { import { pubsub } from '../database/redis'; import withCancel from '../graphql/subscriptionWrapper'; import { killChainPhases } from '../domain/stixEntity'; -import { distributionRelations, loadByGraknId, timeSeriesRelations } from '../database/grakn'; +import { distributionRelations, loadByGraknId, timeSeriesRelations, REL_CONNECTED_SUFFIX } from '../database/grakn'; +import { REL_INDEX_PREFIX } from '../database/elasticSearch'; const stixRelationResolvers = { Query: { @@ -25,6 +26,13 @@ const stixRelationResolvers = { stixRelationsDistribution: async (_, args) => distributionRelations(args), stixRelationsNumber: (_, args) => stixRelationsNumber(args) }, + StixRelationsOrdering: { + toName: `${REL_INDEX_PREFIX}${REL_CONNECTED_SUFFIX}to.name`, + toValidFrom: `${REL_INDEX_PREFIX}${REL_CONNECTED_SUFFIX}to.valid_from`, + toValidUntil: `${REL_INDEX_PREFIX}${REL_CONNECTED_SUFFIX}to.valid_until`, + toPatternType: `${REL_INDEX_PREFIX}${REL_CONNECTED_SUFFIX}to.pattern_type`, + toCreatedAt: `${REL_INDEX_PREFIX}${REL_CONNECTED_SUFFIX}to.created_at` + }, StixRelation: { killChainPhases: rel => killChainPhases(rel.id), from: rel => loadByGraknId(rel.fromId),