diff --git a/opencti-platform/opencti-front/src/private/components/analysis/external_references/StixCoreObjectExternalReferencesLines.js b/opencti-platform/opencti-front/src/private/components/analysis/external_references/StixCoreObjectExternalReferencesLines.js index fbffb6b93ca6..516717d95e85 100644 --- a/opencti-platform/opencti-front/src/private/components/analysis/external_references/StixCoreObjectExternalReferencesLines.js +++ b/opencti-platform/opencti-front/src/private/components/analysis/external_references/StixCoreObjectExternalReferencesLines.js @@ -20,9 +20,14 @@ import DialogActions from '@mui/material/DialogActions'; import { ExpandMoreOutlined, ExpandLessOutlined } from '@mui/icons-material'; import Slide from '@mui/material/Slide'; import { interval } from 'rxjs'; +import { Field, Form, Formik } from 'formik'; +import DialogTitle from '@mui/material/DialogTitle'; +import { includes } from 'ramda'; +import MenuItem from '@mui/material/MenuItem'; +import * as Yup from 'yup'; import inject18n from '../../../../components/i18n'; import { truncate } from '../../../../utils/String'; -import { commitMutation } from '../../../../relay/environment'; +import { commitMutation, MESSAGING$ } from '../../../../relay/environment'; import AddExternalReferences from './AddExternalReferences'; import { externalReferenceMutationRelationDelete } from './AddExternalReferencesLines'; import Security, { @@ -35,6 +40,11 @@ import FileLine from '../../common/files/FileLine'; import { FIVE_SECONDS } from '../../../../utils/Time'; import FileUploader from '../../common/files/FileUploader'; import ExternalReferencePopover from './ExternalReferencePopover'; +import SelectField from '../../../../components/SelectField'; +import { + scopesConn, + stixCoreObjectFilesAndHistoryAskJobImportMutation, +} from '../../common/stix_core_objects/StixCoreObjectFilesAndHistory'; const interval$ = interval(FIVE_SECONDS); @@ -88,6 +98,10 @@ const Transition = React.forwardRef((props, ref) => ( )); Transition.displayName = 'TransitionSlide'; +const importValidation = (t) => Yup.object().shape({ + connector_id: Yup.string().required(t('This field is required')), +}); + class StixCoreObjectExternalReferencesLinesContainer extends Component { constructor(props) { super(props); @@ -98,6 +112,7 @@ class StixCoreObjectExternalReferencesLinesContainer extends Component { removeExternalReference: null, removing: false, expanded: false, + fileToImport: null, }; } @@ -172,13 +187,43 @@ class StixCoreObjectExternalReferencesLinesContainer extends Component { }); } + handleOpenImport(file) { + this.setState({ fileToImport: file }); + } + + handleCloseImport() { + this.setState({ fileToImport: null }); + } + + onSubmitImport(values, { setSubmitting, resetForm }) { + const { stixCoreObjectId } = this.props; + const { fileToImport } = this.state; + commitMutation({ + mutation: stixCoreObjectFilesAndHistoryAskJobImportMutation, + variables: { + fileName: fileToImport.id, + connectorId: values.connector_id, + bypassEntityId: stixCoreObjectId, + }, + onCompleted: () => { + setSubmitting(false); + resetForm(); + this.handleCloseImport(); + MESSAGING$.notifySuccess('Import successfully asked'); + }, + }); + } + render() { const { t, classes, stixCoreObjectId, data } = this.props; - const { expanded } = this.state; + const { expanded, fileToImport } = this.state; const externalReferencesEdges = data && data.stixCoreObject ? data.stixCoreObject.externalReferences.edges : []; const expandable = externalReferencesEdges.length > 7; + const importConnsPerFormat = data.connectorsForImport + ? scopesConn(data.connectorsForImport || []) + : {}; return (
@@ -272,9 +317,17 @@ class StixCoreObjectExternalReferencesLinesContainer extends Component { ))} @@ -425,6 +478,67 @@ class StixCoreObjectExternalReferencesLinesContainer extends Component { + + {({ submitForm, handleReset, isSubmitting }) => ( +
+ + {t('Launch an import')} + + + {data.connectorsForImport.map((connector, i) => { + const disabled = !fileToImport + || (connector.connector_scope.length > 0 + && !includes( + fileToImport.metaData.mimetype, + connector.connector_scope, + )); + return ( + + {connector.name} + + ); + })} + + + + + + + +
+ )} +
); } @@ -508,6 +622,13 @@ const StixCoreObjectExternalReferencesLines = createPaginationContainer( } } } + connectorsForImport { + id + name + active + connector_scope + updated_at + } } `, }, 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 980d8bf551ea..869b85b460cd 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 @@ -143,6 +143,7 @@ class FileLineComponent extends Component { directDownload, handleOpenImport, nested, + workNested, } = this.props; const { lastModifiedSinceMin, uploadStatus, metaData } = file; const { messages, errors } = metaData; @@ -266,7 +267,7 @@ class FileLineComponent extends Component { )} - + ({ nested: { paddingLeft: theme.spacing(4), }, + nestedNested: { + paddingLeft: theme.spacing(8), + }, tooltip: { maxWidth: 600, }, @@ -55,6 +58,7 @@ const FileWorkComponent = (props) => { nsdt, classes, file: { works }, + nested, } = props; const [deleting, setDeleting] = useState(false); const [displayDelete, setDisplayDelete] = useState(null); @@ -129,7 +133,7 @@ const FileWorkComponent = (props) => { dense={true} button={true} divider={true} - classes={{ root: classes.nested }} + classes={{ root: nested ? classes.nestedNested : classes.nested }} disabled={work.status === 'deleting'} > @@ -208,6 +212,7 @@ FileWorkComponent.propTypes = { classes: PropTypes.object, file: PropTypes.object.isRequired, nsdt: PropTypes.func, + nested: PropTypes.bool, }; const FileWork = createFragmentContainer(FileWorkComponent, { diff --git a/opencti-platform/opencti-front/src/private/components/common/stix_core_objects/StixCoreObjectFilesAndHistory.js b/opencti-platform/opencti-front/src/private/components/common/stix_core_objects/StixCoreObjectFilesAndHistory.js index 1e06cffe52db..e0fc190e05ed 100644 --- a/opencti-platform/opencti-front/src/private/components/common/stix_core_objects/StixCoreObjectFilesAndHistory.js +++ b/opencti-platform/opencti-front/src/private/components/common/stix_core_objects/StixCoreObjectFilesAndHistory.js @@ -245,69 +245,67 @@ const StixCoreObjectFilesAndHistory = ({ withoutRelations={withoutRelations} /> -
- - {({ submitForm, handleReset, isSubmitting }) => ( -
- - {t('Launch an import')} - - - {connectorsImport.map((connector, i) => { - const disabled = !fileToImport - || (connector.connector_scope.length > 0 - && !includes( - fileToImport.metaData.mimetype, - connector.connector_scope, - )); - return ( - - {connector.name} - - ); - })} - - - - - - - -
- )} -
-
+ + {({ submitForm, handleReset, isSubmitting }) => ( +
+ + {t('Launch an import')} + + + {connectorsImport.map((connector, i) => { + const disabled = !fileToImport + || (connector.connector_scope.length > 0 + && !includes( + fileToImport.metaData.mimetype, + connector.connector_scope, + )); + return ( + + {connector.name} + + ); + })} + + + + + + + +
+ )} +
{ uploadStatus: 'complete' }; // Trigger a enrich job for import file if needed - if (path.startsWith('import/') && !path.startsWith('import/pending')) { + if (path.startsWith('import/') && !path.startsWith('import/pending') && !path.startsWith('import/External-Reference')) { await uploadJobImport(user, file.id, file.metaData.mimetype, file.metaData.entity_id); } return file; diff --git a/opencti-platform/opencti-graphql/src/resolvers/file.js b/opencti-platform/opencti-graphql/src/resolvers/file.js index bd9f17ed76fb..5da2b783eb6c 100644 --- a/opencti-platform/opencti-graphql/src/resolvers/file.js +++ b/opencti-platform/opencti-graphql/src/resolvers/file.js @@ -4,6 +4,7 @@ import { askJobImport, uploadImport, uploadPending } from '../domain/file'; import { worksForSource } from '../domain/work'; import { stixCoreObjectImportDelete } from '../domain/stixCoreObject'; import { internalLoadById } from '../database/middleware'; +import { ABSTRACT_STIX_DOMAIN_OBJECT } from '../schema/general'; const fileResolvers = { Query: { @@ -15,7 +16,7 @@ const fileResolvers = { works: (file, _, { user }) => worksForSource(user, file.id), metaData: (file, _, { user }) => { if (file.metaData.entity_id) { - return { ...file.metaData, entity: internalLoadById(user, file.metaData.entity_id) }; + return { ...file.metaData, entity: internalLoadById(user, file.metaData.entity_id, { type: ABSTRACT_STIX_DOMAIN_OBJECT }) }; } return file.metaData; },