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/core/startup.py b/backend/core/startup.py
index 3196379e2..2ec1362d4 100644
--- a/backend/core/startup.py
+++ b/backend/core/startup.py
@@ -355,6 +355,10 @@
"view_stakeholder",
"change_stakeholder",
"delete_stakeholder",
+ "add_strategicscenario",
+ "view_strategicscenario",
+ "change_strategicscenario",
+ "delete_strategicscenario",
"add_attackpath",
"view_attackpath",
"change_attackpath",
diff --git a/backend/ebios_rm/migrations/0008_remove_attackpath_ro_to_couple_strategicscenario_and_more.py b/backend/ebios_rm/migrations/0008_remove_attackpath_ro_to_couple_strategicscenario_and_more.py
new file mode 100644
index 000000000..3c98cffa8
--- /dev/null
+++ b/backend/ebios_rm/migrations/0008_remove_attackpath_ro_to_couple_strategicscenario_and_more.py
@@ -0,0 +1,97 @@
+# Generated by Django 5.1.4 on 2024-12-20 08:56
+
+import django.db.models.deletion
+import iam.models
+import uuid
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("ebios_rm", "0007_ebiosrmstudy_meta"),
+ ("iam", "0010_user_preferences"),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name="attackpath",
+ name="ro_to_couple",
+ ),
+ migrations.CreateModel(
+ name="StrategicScenario",
+ fields=[
+ (
+ "id",
+ models.UUIDField(
+ default=uuid.uuid4,
+ editable=False,
+ primary_key=True,
+ serialize=False,
+ ),
+ ),
+ (
+ "created_at",
+ models.DateTimeField(auto_now_add=True, verbose_name="Created at"),
+ ),
+ (
+ "updated_at",
+ models.DateTimeField(auto_now=True, verbose_name="Updated at"),
+ ),
+ (
+ "is_published",
+ models.BooleanField(default=False, verbose_name="published"),
+ ),
+ ("name", models.CharField(max_length=200, verbose_name="Name")),
+ (
+ "description",
+ models.TextField(blank=True, null=True, verbose_name="Description"),
+ ),
+ ("ref_id", models.CharField(blank=True, max_length=100)),
+ (
+ "ebios_rm_study",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="strategic_scenarios",
+ to="ebios_rm.ebiosrmstudy",
+ verbose_name="EBIOS RM study",
+ ),
+ ),
+ (
+ "folder",
+ models.ForeignKey(
+ default=iam.models.Folder.get_root_folder_id,
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="%(class)s_folder",
+ to="iam.folder",
+ ),
+ ),
+ (
+ "ro_to_couple",
+ models.ForeignKey(
+ help_text="RO/TO couple from which the attach path is derived",
+ on_delete=django.db.models.deletion.CASCADE,
+ to="ebios_rm.roto",
+ verbose_name="RO/TO couple",
+ ),
+ ),
+ ],
+ options={
+ "verbose_name": "Strategic Scenario",
+ "verbose_name_plural": "Strategic Scenarios",
+ "ordering": ["created_at"],
+ },
+ ),
+ migrations.AddField(
+ model_name="attackpath",
+ name="strategic_scenario",
+ field=models.ForeignKey(
+ default="",
+ help_text="Strategic scenario from which the attack path is derived",
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="attack_paths",
+ to="ebios_rm.strategicscenario",
+ verbose_name="Strategic scenario",
+ ),
+ preserve_default=False,
+ ),
+ ]
diff --git a/backend/ebios_rm/models.py b/backend/ebios_rm/models.py
index 7f617ee9a..ba5884409 100644
--- a/backend/ebios_rm/models.py
+++ b/backend/ebios_rm/models.py
@@ -212,6 +212,8 @@ class FearedEvent(NameDescriptionMixin, FolderMixin):
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")
verbose_name_plural = _("Feared events")
@@ -309,6 +311,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}"
@@ -338,7 +342,7 @@ def get_pertinence(self):
def get_gravity(self):
gravity = -1
for feared_event in self.feared_events.all():
- if feared_event.gravity > gravity:
+ if feared_event.gravity > gravity and feared_event.is_selected:
gravity = feared_event.gravity
return gravity
@@ -420,11 +424,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()}"
@@ -473,10 +482,11 @@ def get_residual_criticality_display(self) -> str:
)
-class AttackPath(NameDescriptionMixin, FolderMixin):
+class StrategicScenario(NameDescriptionMixin, FolderMixin):
ebios_rm_study = models.ForeignKey(
EbiosRMStudy,
verbose_name=_("EBIOS RM study"),
+ related_name="strategic_scenarios",
on_delete=models.CASCADE,
)
ro_to_couple = models.ForeignKey(
@@ -485,6 +495,51 @@ class AttackPath(NameDescriptionMixin, FolderMixin):
on_delete=models.CASCADE,
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
+ super().save(*args, **kwargs)
+
+ def get_gravity_display(self):
+ if self.ro_to_couple.get_gravity() < 0:
+ return {
+ "abbreviation": "--",
+ "name": "--",
+ "description": "not rated",
+ "value": -1,
+ "hexcolor": "#f9fafb",
+ }
+ risk_matrix = self.ebios_rm_study.parsed_matrix
+ return {
+ **risk_matrix["impact"][self.ro_to_couple.get_gravity()],
+ "value": self.ro_to_couple.get_gravity(),
+ }
+
+
+class AttackPath(NameDescriptionMixin, FolderMixin):
+ ebios_rm_study = models.ForeignKey(
+ EbiosRMStudy,
+ verbose_name=_("EBIOS RM study"),
+ on_delete=models.CASCADE,
+ )
+ strategic_scenario = models.ForeignKey(
+ StrategicScenario,
+ verbose_name=_("Strategic scenario"),
+ on_delete=models.CASCADE,
+ related_name="attack_paths",
+ help_text=_("Strategic scenario from which the attack path is derived"),
+ )
stakeholders = models.ManyToManyField(
Stakeholder,
verbose_name=_("Stakeholders"),
@@ -497,15 +552,25 @@ class AttackPath(NameDescriptionMixin, FolderMixin):
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
self.folder = self.ebios_rm_study.folder
super().save(*args, **kwargs)
+ @property
+ def ro_to_couple(self):
+ return self.strategic_scenario.ro_to_couple
+
@property
def gravity(self):
return self.ro_to_couple.get_gravity()
diff --git a/backend/ebios_rm/serializers.py b/backend/ebios_rm/serializers.py
index 0b0ec8a72..b23c78876 100644
--- a/backend/ebios_rm/serializers.py
+++ b/backend/ebios_rm/serializers.py
@@ -8,6 +8,7 @@
FearedEvent,
RoTo,
Stakeholder,
+ StrategicScenario,
AttackPath,
OperationalScenario,
)
@@ -104,12 +105,8 @@ class Meta:
class StakeholderWriteSerializer(BaseModelSerializer):
- current_criticality = serializers.CharField(
- source="get_current_criticality_display"
- )
- residual_criticality = serializers.CharField(
- source="get_residual_criticality_display"
- )
+ current_criticality = serializers.IntegerField(read_only=True)
+ residual_criticality = serializers.IntegerField(read_only=True)
class Meta:
model = Stakeholder
@@ -136,10 +133,28 @@ class Meta:
fields = "__all__"
+class StrategicScenarioWriteSerializer(BaseModelSerializer):
+ class Meta:
+ model = StrategicScenario
+ exclude = ["created_at", "updated_at", "folder"]
+
+
+class StrategicScenarioReadSerializer(BaseModelSerializer):
+ ebios_rm_study = FieldsRelatedField()
+ folder = FieldsRelatedField()
+ ro_to_couple = FieldsRelatedField()
+ gravity = serializers.JSONField(source="get_gravity_display")
+ attack_paths = FieldsRelatedField(many=True)
+
+ class Meta:
+ model = StrategicScenario
+ fields = "__all__"
+
+
class AttackPathWriteSerializer(BaseModelSerializer):
class Meta:
model = AttackPath
- exclude = ["created_at", "updated_at", "folder"]
+ exclude = ["created_at", "updated_at", "folder", "ebios_rm_study"]
class AttackPathReadSerializer(BaseModelSerializer):
diff --git a/backend/ebios_rm/urls.py b/backend/ebios_rm/urls.py
index 76f7b3e2e..cf7bcaebf 100644
--- a/backend/ebios_rm/urls.py
+++ b/backend/ebios_rm/urls.py
@@ -6,6 +6,7 @@
FearedEventViewSet,
RoToViewSet,
StakeholderViewSet,
+ StrategicScenarioViewSet,
AttackPathViewSet,
OperationalScenarioViewSet,
)
@@ -16,6 +17,9 @@
router.register(r"feared-events", FearedEventViewSet, basename="feared-events")
router.register(r"ro-to", RoToViewSet, basename="ro-to")
router.register(r"stakeholders", StakeholderViewSet, basename="stakeholders")
+router.register(
+ r"strategic-scenarios", StrategicScenarioViewSet, basename="strategic-scenarios"
+)
router.register(r"attack-paths", AttackPathViewSet, basename="attack-paths")
router.register(
r"operational-scenarios",
diff --git a/backend/ebios_rm/views.py b/backend/ebios_rm/views.py
index e84d997eb..871119cd0 100644
--- a/backend/ebios_rm/views.py
+++ b/backend/ebios_rm/views.py
@@ -7,6 +7,7 @@
FearedEvent,
RoTo,
Stakeholder,
+ StrategicScenario,
AttackPath,
OperationalScenario,
)
@@ -109,12 +110,23 @@ def gravity(self, request, pk):
return Response(choices)
+class RoToFilter(df.FilterSet):
+ used = df.BooleanFilter(method="is_used", label="Used")
+
+ def is_used(self, queryset, name, value):
+ if value:
+ return queryset.filter(strategicscenario__isnull=False)
+ return queryset.filter(strategicscenario__isnull=True)
+
+ class Meta:
+ model = RoTo
+ fields = ["ebios_rm_study", "is_selected", "used"]
+
+
class RoToViewSet(BaseModelViewSet):
model = RoTo
- filterset_fields = [
- "ebios_rm_study",
- ]
+ filterset_class = RoToFilter
@action(detail=False, name="Get risk origin choices", url_path="risk-origin")
def risk_origin(self, request):
@@ -134,6 +146,7 @@ class StakeholderViewSet(BaseModelViewSet):
filterset_fields = [
"ebios_rm_study",
+ "is_selected",
]
@action(detail=False, name="Get category choices")
@@ -141,6 +154,14 @@ def category(self, request):
return Response(dict(Stakeholder.Category.choices))
+class StrategicScenarioViewSet(BaseModelViewSet):
+ model = StrategicScenario
+
+ filterset_fields = [
+ "ebios_rm_study",
+ ]
+
+
class AttackPathFilter(df.FilterSet):
used = df.BooleanFilter(method="is_used", label="Used")
diff --git a/enterprise/backend/enterprise_core/settings.py b/enterprise/backend/enterprise_core/settings.py
index 88d783fe1..6e6ab658d 100644
--- a/enterprise/backend/enterprise_core/settings.py
+++ b/enterprise/backend/enterprise_core/settings.py
@@ -142,8 +142,8 @@ def set_ciso_assistant_url(_, __, event_dict):
"iam",
"global_settings",
"tprm",
- "core",
"ebios_rm",
+ "core",
"cal",
"django_filters",
"library",
diff --git a/frontend/messages/en.json b/frontend/messages/en.json
index 794cb8416..3a82e3e6d 100644
--- a/frontend/messages/en.json
+++ b/frontend/messages/en.json
@@ -923,24 +923,24 @@
"ebiosWs5_3": "Define security measures",
"ebiosWs5_4": "Assess and document residual risks",
"ebiosWs5_5": "Establish risk monitoring framework",
- "activity": "Activity",
+ "activity": "Step",
"ebiosRmMatrixHelpText": "Risk matrix used as a reference for the study. Defaults to `urn:intuitem:risk:library:risk-matrix-4x4-ebios-rm`",
- "activityOne": "Activity 1",
- "activityTwo": "Activity 2",
- "activityThree": "Activity 3",
+ "activityOne": "Step 1",
+ "activityTwo": "Step 2",
+ "activityThree": "Step 3",
"ebiosRmStudy": "Ebios RM study",
"qualifications": "Qualifications",
"impacts": "Impacts",
"ebiosRmStudies": "Ebios RM studies",
"bringTheEvidences": "Bring the evidences",
"bringTheEvidencesHelpText": "If disabled, the object will be duplicated without its evidences",
- "gravity": "Gravity",
+ "gravity": "Severity",
"existingControlsHelper": "What do you currently have to manage this risk",
"extraControlsHelper": "What will you do to mitigate this risk",
"existingContextHelper": "Description of the existing mitigations (this field will be deprecated soon)",
"fearedEvent": "Feared event",
"fearedEvents": "Feared events",
- "isSelected": "Is selected",
+ "isSelected": "Selected",
"ebiosRM": "Ebios RM",
"riskOrigin": "Risk origin",
"targetObjective": "Target objective",
@@ -962,7 +962,7 @@
"organizedCrime": "Organized crime",
"terrorist": "Terrorist",
"activist": "Activist",
- "professional": "Professional",
+ "professional": "Competitor",
"amateur": "Amateur",
"avenger": "Avenger",
"pathological": "Pathological",
@@ -1008,6 +1008,10 @@
"minor": "Minor",
"operatingModesDescription": "Operating modes description",
"noStakeholders": "No stakeholders",
+ "strategicScenario": "Strategic scenario",
+ "strategicScenarios": "Strategic scenarios",
+ "goBackToEbiosRmStudy": "Go back to Ebios RM study",
+ "addStrategicScenario": "Add strategic scenario",
"markAsDone": "Mark as done",
"markAsInProgress": "Mark as in progress"
}
diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json
index 6311995ff..a7e674606 100644
--- a/frontend/messages/fr.json
+++ b/frontend/messages/fr.json
@@ -797,6 +797,14 @@
"proof": "Preuve",
"privacy": "Vie privée",
"safety": "Sûreté",
+ "rto": "RTO",
+ "rtoHelpText": "Objectif de temps de récupération",
+ "rpo": "RPO",
+ "rpoHelpText": "Objectif du point de récupération",
+ "mtd": "MTD",
+ "mtdHelpText": "Temps d'arrêt maximal tolérable",
+ "securityObjectives": "Objectifs de sécurité",
+ "disasterRecoveryObjectives": "Objectifs de reprise d'activité",
"noExpirationDateSet": "Aucune date d'expiration définie",
"sumpageTotal": "Total",
"sumpageActive": "Actif",
@@ -858,6 +866,11 @@
"exploitable": "Exploitable",
"mitigated": "Atténuée",
"fixed": "Fixée",
+ "general": "Général",
+ "generalSettingsDescription": "Configurez vos paramètres ici.",
+ "securityObjectiveScale": "Échelle des objectifs de sécurité",
+ "generalSettingsUpdated": "Paramètres mis à jour",
+ "securityObjectiveScaleHelpText": "Choisissez votre échelle d'objectifs de sécurité (1 à 4 par défaut)",
"labels": "Étiquettes",
"addLabel": "Ajouter une étiquette",
"labelsHelpText": "Les étiquettes sont utilisées pour catégoriser et filtrer les éléments.",
@@ -871,12 +884,23 @@
"tags": "Étiquettes",
"addTag": "Ajouter une étiquette",
"tagsHelpText": "Les étiquettes sont utilisées pour classer et filtrer les éléments. Vous pouvez ajouter des étiquettes dans la section Extra",
+ "enabled": "Activé",
+ "hours": "Heures",
+ "minutes": "Minutes",
+ "seconds": "Secondes",
+ "days": "Jours",
+ "milliseconds": "Millisecondes",
"existingMeasures": "Mesures existantes",
+ "youCanSetPasswordHere": "Vous pouvez définir votre mot de passe ici",
"forgotPassword": "Mot de passe oublié",
"ssoSettingsUpdated": "Paramètres SSO mis à jour",
"scoreSemiColon": "Score:",
"mappingInferenceHelpText": "Ces variables sont fixes et ne changeront pas en fonction de la source.",
"priority": "Priorité",
+ "p1": "P1",
+ "p2": "P2",
+ "p3": "P3",
+ "p4": "P4",
"ebiosWs1": "Atelier 1 : Cadrage et Socle de sécurité",
"ebiosWs2": "Atelier 2 : Sources de risque",
"ebiosWs3": "Atelier 3 : Scénarios stratégiques",
@@ -900,22 +924,92 @@
"ebiosWs5_4": "Évaluer et documenter les risques résiduels",
"ebiosWs5_5": "Mettre en place le cadre de suivi des risques",
"activity": "Activité",
+ "ebiosRmMatrixHelpText": "Matrice de risque utilisée pour l'étude. La valeur par défaut est `urn:intuitem:risk:library:risk-matrix-4x4-ebios-rm`",
+ "activityOne": "Activité 1",
+ "activityTwo": "Activité 2",
+ "activityThree": "Activité 3",
+ "ebiosRmStudy": "Étude Ebios RM",
+ "qualifications": "Qualifications",
+ "impacts": "Impacts",
+ "ebiosRmStudies": "Études Ebios RM",
"bringTheEvidences": "Apportez les preuves",
"bringTheEvidencesHelpText": "Si désactivé, l'objet sera dupliqué sans ses preuves",
+ "gravity": "Gravité",
"existingControlsHelper": "Que disposez-vous actuellement pour gérer ce risque",
"extraControlsHelper": "Que ferez-vous pour atténuer ce risque",
"existingContextHelper": "Description des mesures existantes (ce champ sera bientôt obsolète)",
+ "fearedEvent": "Événement redouté",
+ "fearedEvents": "Evénements redoutés",
+ "isSelected": "Retenu",
+ "ebiosRM": "Ebios RM",
+ "riskOrigin": "Source de risque",
+ "targetObjective": "Objectif visé",
+ "motivation": "Motivation",
+ "resources": "Ressources",
+ "pertinence": "Pertinence",
+ "limited": "Limité",
+ "significant": "Significatif",
+ "important": "Important",
+ "unlimited": "Illimité",
+ "strong": "Fort",
+ "irrelevant": "Non pertinent",
+ "partiallyRelevant": "Partiellement pertinent",
+ "fairlyRelevant": "Assez pertinent",
+ "highlyRelevant": "Très pertinent",
+ "roTo": "SR/OV",
+ "roToCouple": "Couple SR/OV",
+ "addRoto": "Ajouter un couple SR/OV",
+ "organizedCrime": "Crime organisé",
+ "terrorist": "Terroriste",
+ "activist": "Activiste",
+ "professional": "Concurrent",
+ "amateur": "Amateur",
+ "avenger": "Vengeur",
+ "pathological": "Pathologique",
+ "currentDependency": "Dépendance actuelle",
+ "currentPenetration": "Pénétration actuelle",
+ "currentMaturity": "Maturité actuelle",
+ "currentTrust": "Confiance actuelle",
+ "residualDependency": "Dépendance résiduelle",
+ "residualPenetration": "Pénétration résiduelle",
+ "residualMaturity": "Maturité résiduelle",
+ "residualTrust": "Confiance résiduelle",
+ "selected": "Retenu",
+ "likelihood": "Vraisemblance",
+ "stakeholders": "Parties prenantes",
+ "addAttackPath": "Ajouter un chemin d'attaque",
+ "attackPath": "Chemin d'attaque",
+ "attackPaths": "Chemins d'attaque",
+ "currentCriticality": "Criticité actuelle",
+ "residualCriticality": "Criticité résiduelle",
+ "notSelected": "Non retenu",
"resetPasswordHere": "Vous pouvez réinitialiser votre mot de passe ici.",
"resetPassword": "Réinitialiser le mot de passe",
- "securityObjectives": "Objectifs de sécurité",
- "disasterRecoveryObjectives": "Objectifs de reprise après sinistre",
- "hours": "Heures",
- "minutes": "Minutes",
- "seconds": "Secondes",
- "rto": "RTO",
- "rtoHelpText": "Objectif de temps de récupération",
- "rpo": "RPO",
- "rpoHelpText": "Objectif du point de récupération",
- "mtd": "MTD",
- "mtdHelpText": "Temps d'arrêt maximal tolérable"
+ "ebiosRm": "Ebios RM",
+ "workshopOne": "Atelier 1",
+ "refIdSemiColon": "ID :",
+ "addFearedEvent": "Ajouter un événement redouté",
+ "addEbiosRMstudy": "Ajouter une étude Ebios RM",
+ "noAuthor": "Aucun auteur attribué",
+ "noReviewer": "Aucun relecteur assigné",
+ "selectAudit": "Sélectionner un audit",
+ "errorAssetGraphMustNotContainCycles": "Le graphe des actifs ne doit pas contenir de boucles.",
+ "addStakeholder": "Ajouter une partie prenante",
+ "operationalScenario": "Scénario opérationnel",
+ "operationalScenarioRefId": "Scénario opérationnel {refId}",
+ "operationalScenarios": "Scénarios opérationnels",
+ "addOperationalScenario": "Ajouter un scénario opérationnel",
+ "workshopFour": "Atelier 4",
+ "noThreat": "Aucune menace",
+ "likely": "Probable",
+ "unlikely": "Peu probable",
+ "veryLikely": "Très probable",
+ "certain": "Certain",
+ "minor": "Mineure",
+ "operatingModesDescription": "Description des modes opératoires",
+ "noStakeholders": "Aucune partie prenante",
+ "strategicScenario": "Scénario stratégique",
+ "strategicScenarios": "Scénarios stratégiques",
+ "goBackToEbiosRmStudy": "Retour à l'étude",
+ "addStrategicScenario": "Ajouter un scénario stratégique"
}
diff --git a/frontend/src/lib/components/CommandPalette/paletteData.ts b/frontend/src/lib/components/CommandPalette/paletteData.ts
index 8903454cc..491235c04 100644
--- a/frontend/src/lib/components/CommandPalette/paletteData.ts
+++ b/frontend/src/lib/components/CommandPalette/paletteData.ts
@@ -34,6 +34,10 @@ export const navigationLinks: NavigationLink[] = [
label: 'riskScenarios',
href: '/risk-scenarios'
},
+ {
+ label: 'ebiosRM',
+ href: '/ebios-rm'
+ },
{
label: 'actionPlan',
href: '/applied-controls'
diff --git a/frontend/src/lib/components/DetailView/DetailView.svelte b/frontend/src/lib/components/DetailView/DetailView.svelte
index 7bf94b2f9..4a2f12545 100644
--- a/frontend/src/lib/components/DetailView/DetailView.svelte
+++ b/frontend/src/lib/components/DetailView/DetailView.svelte
@@ -321,7 +321,7 @@
{:else}
--
{/if}
- {:else if value.id}
+ {:else if value.id && !value.hexcolor}
{@const itemHref = `/${
URL_MODEL_MAP[data.urlModel]['foreignKeyFields']?.find(
(item) => item.field === key
diff --git a/frontend/src/lib/components/Forms/ModelForm.svelte b/frontend/src/lib/components/Forms/ModelForm.svelte
index 939955bce..97393bc4b 100644
--- a/frontend/src/lib/components/Forms/ModelForm.svelte
+++ b/frontend/src/lib/components/Forms/ModelForm.svelte
@@ -47,6 +47,7 @@
import { createModalCache } from '$lib/utils/stores';
import FilteringLabelForm from './ModelForm/FilteringLabelForm.svelte';
import OperationalScenarioForm from './ModelForm/OperationalScenarioForm.svelte';
+ import StrategicScenarioForm from './ModelForm/StrategicScenarioForm.svelte';
export let form: SuperValidated
{m.noStakeholders()}
+{m.noStakeholders()}
{/each}