diff --git a/openbas-api/src/main/java/io/openbas/rest/scenario/form/ScenarioInput.java b/openbas-api/src/main/java/io/openbas/rest/scenario/form/ScenarioInput.java index 7c98c9ff2a..8aec4cba8d 100644 --- a/openbas-api/src/main/java/io/openbas/rest/scenario/form/ScenarioInput.java +++ b/openbas-api/src/main/java/io/openbas/rest/scenario/form/ScenarioInput.java @@ -5,9 +5,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; import io.openbas.database.model.Scenario.SEVERITY; import jakarta.annotation.Nullable; +import jakarta.persistence.Column; +import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; + import java.util.ArrayList; import java.util.List; + import lombok.Data; @Data @@ -45,4 +49,19 @@ public class ScenarioInput { @JsonProperty("scenario_tags") private List tagIds = new ArrayList<>(); + + @JsonProperty("scenario_mail_from") + @Email + @NotBlank + private String from; + + @Column(name = "scenario_reply_to", nullable = false) + @JsonProperty("scenario_mails_reply_to") + private List replyTos = new ArrayList<>(); + + @JsonProperty("scenario_message_header") + private String header; + + @JsonProperty("scenario_message_footer") + private String footer; } diff --git a/openbas-api/src/main/java/io/openbas/rest/settings/response/PlatformSettings.java b/openbas-api/src/main/java/io/openbas/rest/settings/response/PlatformSettings.java index f65cf2f15d..a80b776357 100644 --- a/openbas-api/src/main/java/io/openbas/rest/settings/response/PlatformSettings.java +++ b/openbas-api/src/main/java/io/openbas/rest/settings/response/PlatformSettings.java @@ -6,10 +6,12 @@ import io.openbas.rest.settings.form.PolicyInput; import io.openbas.rest.settings.form.ThemeInput; import jakarta.validation.constraints.NotNull; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; + import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -158,4 +160,11 @@ public Map> getPlatformBannerByLevel() { @NotNull @JsonProperty("expectation_manual_default_score_value") private int expectationDefaultScoreValue; + + // EMAIL CONFIG + @JsonProperty("default_mailer") + private String defaultMailer; + + @JsonProperty("default_reply_to") + private String defaultReplyTo; } diff --git a/openbas-api/src/main/java/io/openbas/service/PlatformSettingsService.java b/openbas-api/src/main/java/io/openbas/service/PlatformSettingsService.java index 51051cb457..1760185b63 100644 --- a/openbas-api/src/main/java/io/openbas/service/PlatformSettingsService.java +++ b/openbas-api/src/main/java/io/openbas/service/PlatformSettingsService.java @@ -22,9 +22,11 @@ import io.openbas.rest.stream.ai.AiConfig; import jakarta.annotation.Resource; import jakarta.validation.constraints.NotBlank; + import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; + import lombok.extern.java.Log; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; @@ -48,9 +50,12 @@ public class PlatformSettingsService { private AiConfig aiConfig; private CalderaExecutorConfig calderaExecutorConfig; - @Resource private OpenBASConfig openBASConfig; - @Resource private ExpectationPropertiesConfig expectationPropertiesConfig; - @Resource private RabbitmqConfig rabbitmqConfig; + @Resource + private OpenBASConfig openBASConfig; + @Resource + private ExpectationPropertiesConfig expectationPropertiesConfig; + @Resource + private RabbitmqConfig rabbitmqConfig; @Autowired public void setOpenCTIConfig(OpenCTIConfig openCTIConfig) { @@ -180,6 +185,8 @@ public PlatformSettings findSettings() { ofNullable(dbSettings.get(DEFAULT_LANG.key())) .map(Setting::getValue) .orElse(DEFAULT_LANG.defaultValue())); + platformSettings.setDefaultMailer(openBASConfig.getDefaultMailer()); + platformSettings.setDefaultReplyTo(openBASConfig.getDefaultReplyTo()); // Build authenticated user settings OpenBASPrincipal user = currentUser(); diff --git a/openbas-front/src/admin/components/common/injects/Injects.tsx b/openbas-front/src/admin/components/common/injects/Injects.tsx index 91500f4c83..cf36a52301 100644 --- a/openbas-front/src/admin/components/common/injects/Injects.tsx +++ b/openbas-front/src/admin/components/common/injects/Injects.tsx @@ -580,7 +580,7 @@ const Injects: FunctionComponent = ({ checked={ (selectAll && !(inject.inject_id in (deSelectedElements || {}))) - || inject.inject_id in (selectedElements || {}) + || inject.inject_id in (selectedElements || {}) } disableRipple /> diff --git a/openbas-front/src/admin/components/scenarios/ScenarioCreation.tsx b/openbas-front/src/admin/components/scenarios/ScenarioCreation.tsx index 7b5471aac0..fb3c08afe9 100644 --- a/openbas-front/src/admin/components/scenarios/ScenarioCreation.tsx +++ b/openbas-front/src/admin/components/scenarios/ScenarioCreation.tsx @@ -1,13 +1,17 @@ import { FunctionComponent, useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import { fetchPlatformParameters } from '../../../actions/Application'; +import { LoggedHelper } from '../../../actions/helper'; import type { ScenarioStore } from '../../../actions/scenarios/Scenario'; import { addScenario } from '../../../actions/scenarios/scenario-actions'; import ButtonCreate from '../../../components/common/ButtonCreate'; import Drawer from '../../../components/common/Drawer'; import { useFormatter } from '../../../components/i18n'; -import type { ScenarioInput } from '../../../utils/api-types'; +import { useHelper } from '../../../store'; +import type { PlatformSettings, ScenarioInput } from '../../../utils/api-types'; import { useAppDispatch } from '../../../utils/hooks'; +import useDataLoader from '../../../utils/hooks/useDataLoader'; import ScenarioForm from './ScenarioForm'; interface Props { @@ -23,6 +27,7 @@ const ScenarioCreation: FunctionComponent = ({ const navigate = useNavigate(); const dispatch = useAppDispatch(); + const onSubmit = (data: ScenarioInput) => { dispatch(addScenario(data)).then( (result: { result: string; entities: { scenarios: Record } }) => { @@ -38,6 +43,29 @@ const ScenarioCreation: FunctionComponent = ({ ); }; + const { settings }: { settings: PlatformSettings } = useHelper((helper: LoggedHelper) => ({ + settings: helper.getPlatformSettings(), + })); + useDataLoader(() => { + dispatch(fetchPlatformParameters()); + }); + + const initialValues: ScenarioInput = { + scenario_name: '', + scenario_category: 'attack-scenario', + scenario_main_focus: 'incident-response', + scenario_severity: 'high', + scenario_subtitle: '', + scenario_description: '', + scenario_external_reference: '', + scenario_external_url: '', + scenario_tags: [], + scenario_message_header: 'SIMULATION HEADER', + scenario_message_footer: 'SIMULATION FOOTER', + scenario_mail_from: settings.default_mailer ? settings.default_mailer : '', + scenario_mails_reply_to: [settings.default_reply_to ? settings.default_reply_to : ''], + }; + return ( <> setOpen(true)} /> @@ -48,6 +76,7 @@ const ScenarioCreation: FunctionComponent = ({ > setOpen(false)} /> diff --git a/openbas-front/src/admin/components/scenarios/ScenarioForm.tsx b/openbas-front/src/admin/components/scenarios/ScenarioForm.tsx index d5f93a601f..0dad01346b 100644 --- a/openbas-front/src/admin/components/scenarios/ScenarioForm.tsx +++ b/openbas-front/src/admin/components/scenarios/ScenarioForm.tsx @@ -1,6 +1,6 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import { Button, MenuItem } from '@mui/material'; -import { FunctionComponent } from 'react'; +import { Alert, AlertTitle, Autocomplete, Button, Chip, Grid, MenuItem, TextField as MuiTextField, Typography } from '@mui/material'; +import { FunctionComponent, useState } from 'react'; import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import { z } from 'zod'; @@ -16,27 +16,20 @@ interface Props { onSubmit: SubmitHandler; handleClose: () => void; editing?: boolean; - initialValues?: ScenarioInput; + disabled?: boolean; + initialValues: ScenarioInput; } const ScenarioForm: FunctionComponent = ({ onSubmit, handleClose, editing, - initialValues = { - scenario_name: '', - scenario_category: 'attack-scenario', - scenario_main_focus: 'incident-response', - scenario_severity: 'high', - scenario_subtitle: '', - scenario_description: '', - scenario_external_reference: '', - scenario_external_url: '', - scenario_tags: [], - }, + initialValues, + disabled, }) => { // Standard hooks const { t } = useFormatter(); + const [inputValue, setInputValue] = useState(''); const { register, @@ -57,12 +50,23 @@ const ScenarioForm: FunctionComponent = ({ scenario_tags: z.string().array().optional(), scenario_external_reference: z.string().optional(), scenario_external_url: z.string().optional(), + scenario_mail_from: z.string().email(t('Should be a valid email address')), + scenario_mails_reply_to: z.array(z.string().email(t('Should be a valid email address'))).optional(), + scenario_message_header: z.string().optional(), + scenario_message_footer: z.string().optional(), }), ), defaultValues: initialValues, }); return (
+ + {t('General')} + = ({ setValue={setValue} askAi={true} /> - - {Array.from(scenarioCategories).map(([key, value]) => ( - - {t(value)} - - ))} - - - - {t('Endpoint Protection')} - - - {t('Web Filtering')} - - - {t('Incident Response')} - - - {t('Standard Operating Procedures')} - - - {t('Crisis Communication')} - - - {t('Strategic Reaction')} - - + + + + {Array.from(scenarioCategories).map(([key, value]) => ( + + {t(value)} + + ))} + + + + + + {t('Endpoint Protection')} + + + {t('Web Filtering')} + + + {t('Incident Response')} + + + {t('Standard Operating Procedures')} + + + {t('Crisis Communication')} + + + {t('Strategic Reaction')} + + + + + = ({ /> )} /> + + {t('Emails and SMS')} + + + + + { + return ( + { + if (undefined !== field.value && inputValue !== '' && !field.value.includes(inputValue)) { + field.onChange([...(field.value || []), inputValue.trim()]); + } + }} + onBlur={field.onBlur} + inputValue={inputValue} + onInputChange={(_event, newInputValue) => { + setInputValue(newInputValue); + }} + disableClearable={true} + renderTags={(tags: string[], getTagProps) => tags.map((email: string, index: number) => { + return ( + { + const newValue = [...(field.value || [])]; + newValue.splice(index, 1); + field.onChange(newValue); + }} + /> + ); + })} + renderInput={params => ( + value != null)?.message ?? '' : ''} + /> + )} + /> + ); + }} + /> + + + {t('If you remove the default email address, the email reception for this simulation / scenario will be disabled.')} + + + +