Skip to content

Commit

Permalink
Merge branch 'main' into model/add-strategic-scenario
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohamed-Hacene committed Dec 19, 2024
2 parents 72c6d5d + a4be0cb commit 658c09a
Show file tree
Hide file tree
Showing 26 changed files with 318 additions and 76 deletions.
66 changes: 66 additions & 0 deletions backend/ebios_rm/migrations/0007_ebiosrmstudy_meta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Generated by Django 5.1.4 on 2024-12-18 01:25

import core.validators
import ebios_rm.models
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("ebios_rm", "0006_alter_attackpath_stakeholders"),
]

operations = [
migrations.AddField(
model_name="ebiosrmstudy",
name="meta",
field=models.JSONField(
default=ebios_rm.models.get_initial_meta,
validators=[
core.validators.JSONSchemaInstanceValidator(
{
"$id": "https://ciso-assistant.com/schemas/ebiosrmstudy/meta.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "Metadata of the EBIOS RM Study",
"properties": {
"workshops": {
"description": "A list of workshops, each containing steps",
"items": {
"additionalProperties": False,
"properties": {
"steps": {
"description": "The list of steps in the workshop",
"items": {
"additionalProperties": False,
"properties": {
"status": {
"description": "The current status of the step",
"enum": [
"to_do",
"in_progress",
"done",
],
"type": "string",
}
},
"required": ["status"],
"type": "object",
},
"type": "array",
}
},
"required": ["steps"],
"type": "object",
},
"type": "array",
}
},
"title": "Metadata",
"type": "object",
}
)
],
verbose_name="Metadata",
),
),
]
88 changes: 87 additions & 1 deletion backend/ebios_rm/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.utils.translation import gettext_lazy as _
from core.base_models import AbstractBaseModel, NameDescriptionMixin, ETADueDateMixin

from core.base_models import AbstractBaseModel, ETADueDateMixin, NameDescriptionMixin
from core.models import (
AppliedControl,
Asset,
Expand All @@ -10,10 +11,42 @@
RiskMatrix,
Threat,
)
from core.validators import (
JSONSchemaInstanceValidator,
)
from iam.models import FolderMixin, User
from tprm.models import Entity
import json

INITIAL_META = {
"workshops": [
{
"steps": [
{"status": "to_do"},
{"status": "to_do"},
{"status": "to_do"},
{"status": "to_do"},
]
},
{"steps": [{"status": "to_do"}, {"status": "to_do"}, {"status": "to_do"}]},
{"steps": [{"status": "to_do"}, {"status": "to_do"}, {"status": "to_do"}]},
{"steps": [{"status": "to_do"}, {"status": "to_do"}]},
{
"steps": [
{"status": "to_do"},
{"status": "to_do"},
{"status": "to_do"},
{"status": "to_do"},
{"status": "to_do"},
]
},
]
}


def get_initial_meta():
return INITIAL_META


class EbiosRMStudy(NameDescriptionMixin, ETADueDateMixin, FolderMixin):
class Status(models.TextChoices):
Expand All @@ -23,6 +56,43 @@ class Status(models.TextChoices):
DONE = "done", _("Done")
DEPRECATED = "deprecated", _("Deprecated")

META_JSONSCHEMA = {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://ciso-assistant.com/schemas/ebiosrmstudy/meta.schema.json",
"title": "Metadata",
"description": "Metadata of the EBIOS RM Study",
"type": "object",
"properties": {
"workshops": {
"type": "array",
"description": "A list of workshops, each containing steps",
"items": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"description": "The list of steps in the workshop",
"items": {
"type": "object",
"properties": {
"status": {
"type": "string",
"description": "The current status of the step",
"enum": ["to_do", "in_progress", "done"],
},
},
"required": ["status"],
"additionalProperties": False,
},
},
},
"required": ["steps"],
"additionalProperties": False,
},
}
},
}

risk_matrix = models.ForeignKey(
RiskMatrix,
on_delete=models.PROTECT,
Expand Down Expand Up @@ -88,6 +158,11 @@ class Status(models.TextChoices):
related_name="reviewers",
)
observation = models.TextField(null=True, blank=True, verbose_name=_("Observation"))
meta = models.JSONField(
default=get_initial_meta,
verbose_name=_("Metadata"),
validators=[JSONSchemaInstanceValidator(META_JSONSCHEMA)],
)

fields_to_check = ["name", "version"]

Expand All @@ -100,6 +175,17 @@ class Meta:
def parsed_matrix(self):
return self.risk_matrix.parse_json_translated()

def update_workshop_step_status(self, workshop: int, step: int, new_status: str):
if workshop < 1 or workshop > 5:
raise ValueError("Workshop must be between 1 and 5")
if step < 1 or step > len(self.meta["workshops"][workshop - 1]["steps"]):
raise ValueError(
f"Worshop {workshop} has only {len(self.meta['workshops'][workshop - 1]['steps'])} steps"
)
status = new_status
self.meta["workshops"][workshop - 1]["steps"][step - 1]["status"] = status
return self.save()


class FearedEvent(NameDescriptionMixin, FolderMixin):
ebios_rm_study = models.ForeignKey(
Expand Down
17 changes: 17 additions & 0 deletions backend/ebios_rm/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
AttackPath,
OperationalScenario,
)
from .serializers import EbiosRMStudyReadSerializer
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from rest_framework.decorators import action
Expand Down Expand Up @@ -67,6 +68,22 @@ def likelihood(self, request, pk):
choices = undefined | _choices
return Response(choices)

@action(
detail=True,
methods=["patch"],
name="Update workshop step status",
url_path="workshop/(?P<workshop>[1-5])/step/(?P<step>[1-5])",
)
def update_workshop_step_status(self, request, pk, workshop, step):
ebios_rm_study: EbiosRMStudy = self.get_object()
workshop = int(workshop)
step = int(step)
# NOTE: For now, just set it as done. Will allow undoing this later.
ebios_rm_study.update_workshop_step_status(
workshop, step, new_status=request.data.get("status", "in_progress")
)
return Response(EbiosRMStudyReadSerializer(ebios_rm_study).data)


class FearedEventViewSet(BaseModelViewSet):
model = FearedEvent
Expand Down
4 changes: 3 additions & 1 deletion frontend/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1011,5 +1011,7 @@
"strategicScenario": "Strategic scenario",
"strategicScenarios": "Strategic scenarios",
"goBackToEbiosRmStudy": "Go back to Ebios RM study",
"addStrategicScenario": "Add strategic scenario"
"addStrategicScenario": "Add strategic scenario",
"markAsDone": "Mark as done",
"markAsInProgress": "Mark as in progress"
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { getModelInfo } from '$lib/utils/crud';
import { modelSchema } from '$lib/utils/schemas';
import type { ModelInfo } from '$lib/utils/types';
import { type Actions } from '@sveltejs/kit';
import { superValidate } from 'sveltekit-superforms';
import { fail, superValidate } from 'sveltekit-superforms';
import { zod } from 'sveltekit-superforms/adapters';
import type { PageServerLoad } from './$types';
import { z } from 'zod';

export const load: PageServerLoad = async ({ params, fetch }) => {
const URLModel = 'ebios-rm';
Expand Down Expand Up @@ -39,5 +40,38 @@ export const actions: Actions = {
action: 'create',
redirectToWrittenObject: true
});
},
changeStepState: async (event) => {
const formData = await event.request.formData();
if (!formData) {
return fail(400, { form: null });
}

const schema = z.object({
workshop: z.number(),
step: z.number(),
status: z.string()
});

const form = await superValidate(formData, zod(schema));

const workshop = formData.get('workshop');
const step = formData.get('step');

const requestInitOptions: RequestInit = {
method: 'PATCH',
body: JSON.stringify(form.data)
};

const endpoint = `${BASE_API_URL}/ebios-rm/studies/${event.params.id}/workshop/${workshop}/step/${step}/`;
const res = await event.fetch(endpoint, requestInitOptions);

if (!res.ok) {
const response = await res.text();
console.error(response);
return fail(400, { form });
}

return { success: true, form };
}
};
Loading

0 comments on commit 658c09a

Please sign in to comment.