Skip to content

Commit

Permalink
[backend/frontend] Introduce TAXII push endpoints (#8932)
Browse files Browse the repository at this point in the history
  • Loading branch information
richard-julien authored Jan 3, 2025
1 parent 8a66e8b commit 3497aa7
Show file tree
Hide file tree
Showing 28 changed files with 1,563 additions and 44 deletions.
3 changes: 2 additions & 1 deletion opencti-platform/opencti-front/lang/front/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -2630,7 +2630,8 @@
"TAXII Collection": "TAXII Sammlung",
"TAXII collections": "TAXII-Sammlungen",
"TAXII Feeds": "TAXII Feeds",
"TAXII feeds": "TAXII-Feeds",
"TAXII feeds": "TAXII Feeds",
"TAXII push": "TAXII drücken",
"TAXII server URL": "TAXII-Server-URL",
"TAXII version": "TAXII-Version",
"Taxonomies": "Taxonomien",
Expand Down
1 change: 1 addition & 0 deletions opencti-platform/opencti-front/lang/front/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2631,6 +2631,7 @@
"TAXII collections": "TAXII collections",
"TAXII Feeds": "TAXII Feeds",
"TAXII feeds": "TAXII feeds",
"TAXII push": "TAXII push",
"TAXII server URL": "TAXII server URL",
"TAXII version": "TAXII version",
"Taxonomies": "Taxonomies",
Expand Down
1 change: 1 addition & 0 deletions opencti-platform/opencti-front/lang/front/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -2631,6 +2631,7 @@
"TAXII collections": "Colecciones de TAXII",
"TAXII Feeds": "Suministros TAXII",
"TAXII feeds": "Fuentes TAXII",
"TAXII push": "Empuje TAXII",
"TAXII server URL": "URL del servidor TAXII",
"TAXII version": "Versión de TAXII",
"Taxonomies": "Taxonomías",
Expand Down
1 change: 1 addition & 0 deletions opencti-platform/opencti-front/lang/front/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -2631,6 +2631,7 @@
"TAXII collections": "Collections TAXII",
"TAXII Feeds": "Flux TAXII",
"TAXII feeds": "Flux TAXII",
"TAXII push": "Push TAXII",
"TAXII server URL": "URL du serveur TAXII",
"TAXII version": "Version de TAXII",
"Taxonomies": "Taxonomies",
Expand Down
1 change: 1 addition & 0 deletions opencti-platform/opencti-front/lang/front/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -2631,6 +2631,7 @@
"TAXII collections": "TAXIIコレクション",
"TAXII Feeds": "TAXII フィード",
"TAXII feeds": "TAXIIフィード",
"TAXII push": "TAXIIプッシュ",
"TAXII server URL": "TAXIIサーバーURL",
"TAXII version": "TAXIIバージョン",
"Taxonomies": "タクソノミー",
Expand Down
1 change: 1 addition & 0 deletions opencti-platform/opencti-front/lang/front/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -2631,6 +2631,7 @@
"TAXII collections": "TAXII 컬렉션",
"TAXII Feeds": "TAXII 피드",
"TAXII feeds": "TAXII 피드",
"TAXII push": "TAXII 푸시",
"TAXII server URL": "TAXII 서버 URL",
"TAXII version": "TAXII 버전",
"Taxonomies": "분류",
Expand Down
1 change: 1 addition & 0 deletions opencti-platform/opencti-front/lang/front/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -2631,6 +2631,7 @@
"TAXII collections": "TAXII集合",
"TAXII Feeds": "TAXII订阅源",
"TAXII feeds": "TAXII 源",
"TAXII push": "TAXII 推动",
"TAXII server URL": "TAXII 服务器 URL",
"TAXII version": "TAXII 版本",
"Taxonomies": "分类法",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ const IngestionMenu = () => {
path: '/dashboard/data/ingestion/taxii',
label: 'TAXII Feeds',
},
{
path: '/dashboard/data/ingestion/collection',
label: 'TAXII Push',
},
{
path: '/dashboard/data/ingestion/rss',
label: 'RSS Feeds',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';
import makeStyles from '@mui/styles/makeStyles';
import { QueryRenderer } from '../../../relay/environment';
import ListLines from '../../../components/list_lines/ListLines';
import IngestionTaxiiCollectionLines, { IngestionTaxiiCollectionLinesQuery } from './ingestionTaxiiCollection/IngestionTaxiiCollectionLines';
import IngestionTaxiiCollectionCreation from './ingestionTaxiiCollection/IngestionTaxiiCollectionCreation';
import { usePaginationLocalStorage } from '../../../utils/hooks/useLocalStorage';
import { useFormatter } from '../../../components/i18n';
import IngestionMenu from './IngestionMenu';
import Breadcrumbs from '../../../components/Breadcrumbs';
import Security from '../../../utils/Security';
import { INGESTION_SETINGESTIONS } from '../../../utils/hooks/useGranted';

const LOCAL_STORAGE_KEY = 'ingestionTaxii';

// Deprecated - https://mui.com/system/styles/basics/
// Do not use it for new code.
const useStyles = makeStyles(() => ({
container: {
margin: 0,
padding: '0 200px 50px 0',
},
}));

const IngestionTaxiiCollections = () => {
const classes = useStyles();
const { t_i18n } = useFormatter();
const {
viewStorage,
paginationOptions,
helpers: storageHelpers,
} = usePaginationLocalStorage(LOCAL_STORAGE_KEY, {
sortBy: 'name',
orderAsc: false,
searchTerm: '',
});
const dataColumns = {
name: {
label: 'Name',
width: '15%',
isSortable: true,
},
id: {
label: 'Push Collection URI',
width: '65%',
isSortable: false,
},
ingestion_running: {
label: 'Status',
width: '10%',
isSortable: false,
},
};
return (
<div className={classes.container}>
<Breadcrumbs elements={[{ label: t_i18n('Data') }, { label: t_i18n('Ingestion') }, { label: t_i18n('TAXII push'), current: true }]} />
<IngestionMenu/>
<ListLines
helpers={storageHelpers}
sortBy={viewStorage.sortBy}
orderAsc={viewStorage.orderAsc}
dataColumns={dataColumns}
handleSort={storageHelpers.handleSort}
handleSearch={storageHelpers.handleSearch}
displayImport={false}
secondaryAction={true}
keyword={viewStorage.searchTerm}
>
<QueryRenderer
query={IngestionTaxiiCollectionLinesQuery}
variables={{ count: 200, ...paginationOptions }}
render={({ props }) => (
<IngestionTaxiiCollectionLines
data={props}
paginationOptions={paginationOptions}
refetchPaginationOptions={{ count: 200, ...paginationOptions }}
dataColumns={dataColumns}
initialLoading={props === null}
/>
)}
/>
</ListLines>
<Security needs={[INGESTION_SETINGESTIONS]}>
<IngestionTaxiiCollectionCreation paginationOptions={paginationOptions} />
</Security>
</div>
);
};

export default IngestionTaxiiCollections;
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const Feed = lazy(() => import('./Feed'));
const Sync = lazy(() => import('./Sync'));
const IngestionRss = lazy(() => import('./IngestionRss'));
const IngestionTaxiis = lazy(() => import('./IngestionTaxiis'));
const IngestionTaxiiCollections = lazy(() => import('./IngestionTaxiiCollections'));
const Playbooks = lazy(() => import('./Playbooks'));
const RootPlaybook = lazy(() => import('./playbooks/Root'));
const RootImport = lazy(() => import('./import/Root'));
Expand Down Expand Up @@ -101,6 +102,10 @@ const Root = () => {
path="/ingestion/taxii"
element={boundaryWrapper(IngestionTaxiis)}
/>
<Route
path="/ingestion/collection"
element={boundaryWrapper(IngestionTaxiiCollections)}
/>
<Route
path="/ingestion/csv"
element={boundaryWrapper(IngestionCsv)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import React from 'react';
import * as PropTypes from 'prop-types';
import { Field, Form, Formik } from 'formik';
import withStyles from '@mui/styles/withStyles';
import Button from '@mui/material/Button';
import * as Yup from 'yup';
import { graphql } from 'react-relay';
import * as R from 'ramda';
import Drawer, { DrawerVariant } from '../../common/drawer/Drawer';
import inject18n from '../../../../components/i18n';
import { commitMutation } from '../../../../relay/environment';
import TextField from '../../../../components/TextField';
import CreatorField from '../../common/form/CreatorField';
import { fieldSpacingContainerStyle } from '../../../../utils/field';
import { insertNode } from '../../../../utils/store';
import SwitchField from '../../../../components/fields/SwitchField';

const styles = (theme) => ({
buttons: {
marginTop: 20,
textAlign: 'right',
},
button: {
marginLeft: theme.spacing(2),
},
});

const IngestionTaxiiCollectionCreationMutation = graphql`
mutation IngestionTaxiiCollectionCreationMutation($input: IngestionTaxiiCollectionAddInput!) {
ingestionTaxiiCollectionAdd(input: $input) {
...IngestionTaxiiCollectionLine_node
}
}
`;

const ingestionTaxiiCollectionCreationValidation = (t) => Yup.object().shape({
name: Yup.string().required(t('This field is required')),
description: Yup.string().nullable(),
user_id: Yup.object().nullable(),
confidence_to_score: Yup.bool().nullable(),
});

const IngestionTaxiiCollectionCreation = (props) => {
const { t, classes } = props;
const onSubmit = (values, { setSubmitting, resetForm }) => {
const input = {
name: values.name,
description: values.description,
confidence_to_score: values.confidence_to_score,
user_id: values.user_id?.value,
};
commitMutation({
mutation: IngestionTaxiiCollectionCreationMutation,
variables: {
input,
},
updater: (store) => {
insertNode(
store,
'Pagination_ingestionTaxiiCollections',
props.paginationOptions,
'ingestionTaxiiCollectionAdd',
);
},
setSubmitting,
onCompleted: () => {
setSubmitting(false);
resetForm();
},
});
};

return (
<Drawer title={t('Create a TAXII Push ingester')} variant={DrawerVariant.createWithPanel}>
{({ onClose }) => (
<Formik
initialValues={{
name: '',
description: '',
user_id: '',
confidence_to_score: false,
}}
validationSchema={ingestionTaxiiCollectionCreationValidation(t)}
onSubmit={onSubmit}
onReset={onClose}
>
{({ submitForm, handleReset, isSubmitting }) => (
<Form>
<Field
component={TextField}
variant="standard"
name="name"
label={t('Name')}
fullWidth={true}
/>
<Field
component={TextField}
variant="standard"
name="description"
label={t('Description')}
fullWidth={true}
style={fieldSpacingContainerStyle}
/>
<CreatorField
name="user_id"
label={t('User responsible for data creation (empty = System)')}
containerStyle={fieldSpacingContainerStyle}
showConfidence
/>
<Field
component={SwitchField}
type="checkbox"
name="confidence_to_score"
label={t('Copy confidence level to OpenCTI scores for indicators')}
containerstyle={fieldSpacingContainerStyle}
/>
<div className={classes.buttons}>
<Button
variant="contained"
onClick={handleReset}
disabled={isSubmitting}
classes={{ root: classes.button }}
>
{t('Cancel')}
</Button>
<Button
variant="contained"
color="secondary"
onClick={submitForm}
disabled={isSubmitting}
classes={{ root: classes.button }}
>
{t('Create')}
</Button>
</div>
</Form>
)}
</Formik>
)}
</Drawer>
);
};

IngestionTaxiiCollectionCreation.propTypes = {
paginationOptions: PropTypes.object,
classes: PropTypes.object,
theme: PropTypes.object,
t: PropTypes.func,
};

export default R.compose(
inject18n,
withStyles(styles, { withTheme: true }),
)(IngestionTaxiiCollectionCreation);
Loading

0 comments on commit 3497aa7

Please sign in to comment.