From 8c972f556f2df70aa43e4aebf9a8290072757332 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Mon, 16 Dec 2024 12:51:21 +0100 Subject: [PATCH 1/4] Update AttackPath and OperationalScenario models - Make the relationship between AttackPath and OperationalScenario one-to-one - Rename OperationalScenario.description to operating_modes_description - Use NameDescriptionMixin to AttackPath --- backend/ebios_rm/models.py | 18 +++++++++++------- backend/ebios_rm/serializers.py | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) 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") From e44868ce1822be73d50c42277ecd62b9a200e8dd Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Mon, 16 Dec 2024 12:59:44 +0100 Subject: [PATCH 2/4] chore: Make migrations --- ...erationalscenario_attack_paths_and_more.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 backend/ebios_rm/migrations/0005_remove_operationalscenario_attack_paths_and_more.py 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..93ed499fd --- /dev/null +++ b/backend/ebios_rm/migrations/0005_remove_operationalscenario_attack_paths_and_more.py @@ -0,0 +1,50 @@ +# 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'), + ), + ] From 6f4786fdf8e406c014ea18af48bba855b5894644 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Mon, 16 Dec 2024 13:01:07 +0100 Subject: [PATCH 3/4] Update frontend CRUD data --- frontend/src/lib/utils/crud.ts | 2 +- frontend/src/lib/utils/schemas.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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), From f9c769b28541147b0b28bea21186cb2a406f08d8 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Mon, 16 Dec 2024 13:03:23 +0100 Subject: [PATCH 4/4] chore: ruff format --- ...erationalscenario_attack_paths_and_more.py | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) 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 index 93ed499fd..2170a60c5 100644 --- 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 @@ -5,46 +5,55 @@ class Migration(migrations.Migration): - dependencies = [ - ('ebios_rm', '0004_remove_roto_pertinence_alter_roto_feared_events'), + ("ebios_rm", "0004_remove_roto_pertinence_alter_roto_feared_events"), ] operations = [ migrations.RemoveField( - model_name='operationalscenario', - name='attack_paths', + model_name="operationalscenario", + name="attack_paths", ), migrations.RemoveField( - model_name='operationalscenario', - name='description', + model_name="operationalscenario", + name="description", ), migrations.AddField( - model_name='attackpath', - name='name', - field=models.CharField(default='', max_length=200, verbose_name='Name'), + 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', + 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'), + 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'), + 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'), + model_name="attackpath", + name="description", + field=models.TextField(blank=True, null=True, verbose_name="Description"), ), ]