diff --git a/backend/ebios_rm/migrations/0005_remove_operationalscenario_attack_paths_and_more.py b/backend/ebios_rm/migrations/0005_remove_operationalscenario_attack_paths_and_more.py new file mode 100644 index 000000000..2170a60c5 --- /dev/null +++ b/backend/ebios_rm/migrations/0005_remove_operationalscenario_attack_paths_and_more.py @@ -0,0 +1,59 @@ +# Generated by Django 5.1.4 on 2024-12-16 11:59 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("ebios_rm", "0004_remove_roto_pertinence_alter_roto_feared_events"), + ] + + operations = [ + migrations.RemoveField( + model_name="operationalscenario", + name="attack_paths", + ), + migrations.RemoveField( + model_name="operationalscenario", + name="description", + ), + migrations.AddField( + model_name="attackpath", + name="name", + field=models.CharField(default="", max_length=200, verbose_name="Name"), + preserve_default=False, + ), + migrations.AddField( + model_name="attackpath", + name="ref_id", + field=models.CharField(blank=True, max_length=100), + ), + migrations.AddField( + model_name="operationalscenario", + name="attack_path", + field=models.OneToOneField( + default="", + on_delete=django.db.models.deletion.CASCADE, + related_name="operational_scenario", + to="ebios_rm.attackpath", + verbose_name="Attack path", + ), + preserve_default=False, + ), + migrations.AddField( + model_name="operationalscenario", + name="operating_modes_description", + field=models.TextField( + default="", + help_text="Description of the operating modes of the operational scenario", + verbose_name="Operating modes description", + ), + preserve_default=False, + ), + migrations.AlterField( + model_name="attackpath", + name="description", + field=models.TextField(blank=True, null=True, verbose_name="Description"), + ), + ] diff --git a/backend/ebios_rm/models.py b/backend/ebios_rm/models.py index dbe56bfb8..a93f1d4b3 100644 --- a/backend/ebios_rm/models.py +++ b/backend/ebios_rm/models.py @@ -363,7 +363,7 @@ def residual_criticality(self): ) -class AttackPath(AbstractBaseModel, FolderMixin): +class AttackPath(NameDescriptionMixin, FolderMixin): ebios_rm_study = models.ForeignKey( EbiosRMStudy, verbose_name=_("EBIOS RM study"), @@ -382,7 +382,7 @@ class AttackPath(AbstractBaseModel, FolderMixin): help_text=_("Stakeholders leveraged by the attack path"), ) - description = models.TextField(verbose_name=_("Description")) + ref_id = models.CharField(max_length=100, blank=True) is_selected = models.BooleanField(verbose_name=_("Is selected"), default=False) justification = models.TextField(verbose_name=_("Justification"), blank=True) @@ -406,11 +406,12 @@ class OperationalScenario(AbstractBaseModel, FolderMixin): related_name="operational_scenarios", on_delete=models.CASCADE, ) - attack_paths = models.ManyToManyField( + attack_path = models.OneToOneField( AttackPath, - verbose_name=_("Attack paths"), - related_name="operational_scenarios", - help_text=_("Attack paths that are pertinent to the operational scenario"), + verbose_name=_("Attack path"), + on_delete=models.CASCADE, + related_name="operational_scenario", + blank=False, ) threats = models.ManyToManyField( Threat, @@ -420,7 +421,10 @@ class OperationalScenario(AbstractBaseModel, FolderMixin): help_text=_("Threats leveraged by the operational scenario"), ) - description = models.TextField(verbose_name=_("Description")) + operating_modes_description = models.TextField( + verbose_name=_("Operating modes description"), + help_text=_("Description of the operating modes of the operational scenario"), + ) likelihood = models.SmallIntegerField(default=-1, verbose_name=_("Likelihood")) is_selected = models.BooleanField(verbose_name=_("Is selected"), default=False) justification = models.TextField(verbose_name=_("Justification"), blank=True) diff --git a/backend/ebios_rm/serializers.py b/backend/ebios_rm/serializers.py index 3eed45bf0..6b69798b9 100644 --- a/backend/ebios_rm/serializers.py +++ b/backend/ebios_rm/serializers.py @@ -157,7 +157,7 @@ class OperationalScenarioReadSerializer(BaseModelSerializer): str = serializers.CharField(source="__str__") ebios_rm_study = FieldsRelatedField() folder = FieldsRelatedField() - attack_paths = FieldsRelatedField(many=True) + attack_path = FieldsRelatedField() threats = FieldsRelatedField(many=True) likelihood = serializers.JSONField(source="get_likelihood_display") diff --git a/frontend/src/lib/utils/crud.ts b/frontend/src/lib/utils/crud.ts index b9a6d9357..e6b4c3f30 100644 --- a/frontend/src/lib/utils/crud.ts +++ b/frontend/src/lib/utils/crud.ts @@ -700,7 +700,7 @@ export const URL_MODEL_MAP: ModelMap = { foreignKeyFields: [ { field: 'ebios_rm_study', urlModel: 'ebios-rm' }, { field: 'threats', urlModel: 'threats' }, - { field: 'attack_paths', urlModel: 'attack-paths' } + { field: 'attack_path', urlModel: 'attack-paths' } ], selectFields: [{ field: 'likelihood', valueType: 'number' }] } diff --git a/frontend/src/lib/utils/schemas.ts b/frontend/src/lib/utils/schemas.ts index 7872fa503..d45a34522 100644 --- a/frontend/src/lib/utils/schemas.ts +++ b/frontend/src/lib/utils/schemas.ts @@ -456,7 +456,7 @@ export const AttackPathSchema = z.object({ export const operationalScenarioSchema = z.object({ ebios_rm_study: z.string(), - attack_paths: z.string().uuid().array(), + attack_path: z.string().uuid(), threats: z.string().uuid().optional().array().optional(), description: z.string(), likelihood: z.number().optional().default(-1),