From a8c79639eda2dace130327d4b51e6bb71f724c37 Mon Sep 17 00:00:00 2001
From: Mohamed-Hacene
Date: Tue, 17 Dec 2024 20:46:00 +0100
Subject: [PATCH] feat: add ebios objects scope and fields to check
---
backend/core/base_models.py | 20 ++++++++++++-------
backend/ebios_rm/models.py | 19 ++++++++++++++++++
frontend/src/lib/utils/schemas.ts | 2 +-
.../[id=uuid]/+page.svelte | 2 +-
4 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/backend/core/base_models.py b/backend/core/base_models.py
index 264567870..5e1240797 100644
--- a/backend/core/base_models.py
+++ b/backend/core/base_models.py
@@ -46,13 +46,19 @@ def is_unique_in_scope(self, scope: models.QuerySet, fields_to_check: list) -> b
# to avoid false positives as a result of the object being compared to itself
if self.pk:
scope = scope.exclude(pk=self.pk)
- return not scope.filter(
- **{
- f"{field}__iexact": getattr(self, field)
- for field in fields_to_check
- if hasattr(self, field)
- }
- ).exists()
+ filters = {}
+ for field in fields_to_check:
+ if hasattr(self, field):
+ field_value = getattr(self, field)
+ model_field = self._meta.get_field(field)
+
+ # Use the appropriate lookup based on the field type
+ if isinstance(model_field, models.ForeignKey):
+ filters[f"{field}__exact"] = field_value
+ else:
+ filters[f"{field}__iexact"] = field_value
+
+ return not scope.filter(**filters).exists()
def display_path(self):
pass
diff --git a/backend/ebios_rm/models.py b/backend/ebios_rm/models.py
index 35361e0db..af9277a9c 100644
--- a/backend/ebios_rm/models.py
+++ b/backend/ebios_rm/models.py
@@ -126,6 +126,8 @@ class FearedEvent(NameDescriptionMixin, FolderMixin):
gravity = models.SmallIntegerField(default=-1, verbose_name=_("Gravity"))
is_selected = models.BooleanField(verbose_name=_("Is selected"), default=False)
justification = models.TextField(verbose_name=_("Justification"), blank=True)
+
+ fields_to_check = ["name", "ref_id"]
class Meta:
verbose_name = _("Feared event")
@@ -223,6 +225,8 @@ class Pertinence(models.IntegerChoices):
)
is_selected = models.BooleanField(verbose_name=_("Is selected"), default=False)
justification = models.TextField(verbose_name=_("Justification"), blank=True)
+
+ fields_to_check = ["target_objective", "risk_origin"]
def __str__(self) -> str:
return f"{self.get_risk_origin_display()} - {self.target_objective}"
@@ -334,11 +338,16 @@ class Category(models.TextChoices):
is_selected = models.BooleanField(verbose_name=_("Is selected"), default=False)
justification = models.TextField(verbose_name=_("Justification"), blank=True)
+
+ fields_to_check = ["entity", "category"]
class Meta:
verbose_name = _("Stakeholder")
verbose_name_plural = _("Stakeholders")
ordering = ["created_at"]
+
+ def get_scope(self):
+ return self.__class__.objects.filter(ebios_rm_study=self.ebios_rm_study)
def __str__(self):
return f"{self.entity.name} - {self.get_category_display()}"
@@ -388,11 +397,16 @@ class StrategicScenario(NameDescriptionMixin, FolderMixin):
help_text=_("RO/TO couple from which the attach path is derived"),
)
ref_id = models.CharField(max_length=100, blank=True)
+
+ fields_to_check = ["name", "ref_id"]
class Meta:
verbose_name = _("Strategic Scenario")
verbose_name_plural = _("Strategic Scenarios")
ordering = ["created_at"]
+
+ def get_scope(self):
+ return self.__class__.objects.filter(ebios_rm_study=self.ebios_rm_study)
def save(self, *args, **kwargs):
self.folder = self.ebios_rm_study.folder
@@ -423,11 +437,16 @@ class AttackPath(NameDescriptionMixin, FolderMixin):
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)
+
+ fields_to_check = ["name", "ref_id"]
class Meta:
verbose_name = _("Attack path")
verbose_name_plural = _("Attack paths")
ordering = ["created_at"]
+
+ def get_scope(self):
+ return self.__class__.objects.filter(ebios_rm_study=self.ebios_rm_study)
def save(self, *args, **kwargs):
self.ebios_rm_study = self.strategic_scenario.ebios_rm_study
diff --git a/frontend/src/lib/utils/schemas.ts b/frontend/src/lib/utils/schemas.ts
index 2d09b8766..ab3c76d21 100644
--- a/frontend/src/lib/utils/schemas.ts
+++ b/frontend/src/lib/utils/schemas.ts
@@ -447,13 +447,13 @@ export const StakeholderSchema = z.object({
export const StrategicScenarioSchema = z.object({
...NameDescriptionMixin,
+ ebios_rm_study: z.string(),
ro_to_couple: z.string().uuid(),
ref_id: z.string().optional()
});
export const AttackPathSchema = z.object({
...NameDescriptionMixin,
- ebios_rm_study: z.string(),
strategic_scenario: z.string().uuid(),
stakeholders: z.string().uuid().optional().array().optional(),
is_selected: z.boolean().optional(),
diff --git a/frontend/src/routes/(app)/(internal)/operational-scenarios/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(internal)/operational-scenarios/[id=uuid]/+page.svelte
index 0a14446a3..cbd6b572f 100644
--- a/frontend/src/routes/(app)/(internal)/operational-scenarios/[id=uuid]/+page.svelte
+++ b/frontend/src/routes/(app)/(internal)/operational-scenarios/[id=uuid]/+page.svelte
@@ -122,7 +122,7 @@
>
{:else}
- {m.noStakeholders()}
+ {m.noStakeholders()}
{/each}