Skip to content

Commit

Permalink
Workshop 3 (#1139)
Browse files Browse the repository at this point in the history
  • Loading branch information
nas-tabchiche authored Dec 10, 2024
2 parents a56a1a6 + 96ba91c commit 020b34a
Show file tree
Hide file tree
Showing 16 changed files with 634 additions and 9 deletions.
6 changes: 6 additions & 0 deletions backend/ebios_rm/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ class Pertinence(models.IntegerChoices):
is_selected = models.BooleanField(verbose_name=_("Is selected"), default=False)
justification = models.TextField(verbose_name=_("Justification"), blank=True)

def __str__(self) -> str:
return f"{self.get_risk_origin_display()} - {self.target_objective}"

class Meta:
verbose_name = _("RO/TO couple")
verbose_name_plural = _("RO/TO couples")
Expand Down Expand Up @@ -322,6 +325,9 @@ class Meta:
verbose_name_plural = _("Stakeholders")
ordering = ["created_at"]

def __str__(self):
return f"{self.entity.name} - {self.get_category_display()}"

def save(self, *args, **kwargs):
self.folder = self.ebios_rm_study.folder
super().save(*args, **kwargs)
Expand Down
2 changes: 2 additions & 0 deletions backend/ebios_rm/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ class AttackPathReadSerializer(BaseModelSerializer):
folder = FieldsRelatedField()
ro_to_couple = FieldsRelatedField()
stakeholders = FieldsRelatedField(many=True)
risk_origin = serializers.CharField(source="ro_to_couple.get_risk_origin_display")
target_objective = serializers.CharField(source="ro_to_couple.target_objective")

class Meta:
model = AttackPath
Expand Down
8 changes: 8 additions & 0 deletions backend/ebios_rm/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ def pertinence(self, request):
class StakeholderViewSet(BaseModelViewSet):
model = Stakeholder

filterset_fields = [
"ebios_rm_study",
]

@action(detail=False, name="Get category choices")
def category(self, request):
return Response(dict(Stakeholder.Category.choices))
Expand All @@ -109,6 +113,10 @@ def category(self, request):
class AttackPathViewSet(BaseModelViewSet):
model = AttackPath

filterset_fields = [
"ebios_rm_study",
]


class OperationalScenarioViewSet(BaseModelViewSet):
model = OperationalScenario
17 changes: 16 additions & 1 deletion frontend/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -955,12 +955,27 @@
"fairlyRelevant": "Fairly relevant",
"highlyRelevant": "Highly relevant",
"roTo": "RO/TO",
"roToCouple": "RO/TO couple",
"addRoto": "Add RO/TO couple",
"organizedCrime": "Organized crime",
"terrorist": "Terrorist",
"activist": "Activist",
"professional": "Professional",
"amateur": "Amateur",
"avenger": "Avenger",
"pathological": "Pathological"
"pathological": "Pathological",
"currentDependency": "Current dependency",
"currentPenetration": "Current penetration",
"currentMaturity": "Current maturity",
"currentTrust": "Current trust",
"residualDependency": "Residual dependency",
"residualPenetration": "Residual penetration",
"residualMaturity": "Residual maturity",
"residualTrust": "Residual trust",
"selected": "Selected",
"likelihood": "Likelihood",
"stakeholders": "Stakeholders",
"addAttackPath": "Add attack path",
"currentCriticality": "Current criticality",
"residualCriticality": "Residual criticality"
}
6 changes: 6 additions & 0 deletions frontend/src/lib/components/Forms/ModelForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import EbiosRmForm from './ModelForm/EbiosRmForm.svelte';
import FearedEventForm from './ModelForm/FearedEventForm.svelte';
import RoToForm from './ModelForm/RoToForm.svelte';
import StakeholderForm from './ModelForm/StakeholderForm.svelte';
import AttackPathForm from './ModelForm/AttackPathForm.svelte';
import AutocompleteSelect from './AutocompleteSelect.svelte';
Expand Down Expand Up @@ -264,6 +266,10 @@
<FearedEventForm {form} {model} {cacheLocks} {formDataCache} {initialData} />
{:else if URLModel === 'ro-to'}
<RoToForm {form} {model} {cacheLocks} {formDataCache} {initialData} />
{:else if URLModel === 'stakeholders'}
<StakeholderForm {form} {model} {cacheLocks} {formDataCache} {context} />
{:else if URLModel === 'attack-paths'}
<AttackPathForm {form} {model} {cacheLocks} {formDataCache} {initialData} />
{/if}
<div class="flex flex-row justify-between space-x-4">
{#if closeModal}
Expand Down
50 changes: 50 additions & 0 deletions frontend/src/lib/components/Forms/ModelForm/AttackPathForm.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script lang="ts">
import type { SuperValidated } from 'sveltekit-superforms';
import type { ModelInfo, CacheLock } from '$lib/utils/types';
import TextField from '$lib/components/Forms/TextField.svelte';
import AutocompleteSelect from '$lib/components/Forms/AutocompleteSelect.svelte';
import Select from '$lib/components/Forms/Select.svelte';
import * as m from '$paraglide/messages.js';
import { getOptions } from '$lib/utils/crud';
import TextArea from '../TextArea.svelte';
import NumberField from '../NumberField.svelte';
import Checkbox from '../Checkbox.svelte';
export let form: SuperValidated<any>;
export let model: ModelInfo;
export let cacheLocks: Record<string, CacheLock> = {};
export let formDataCache: Record<string, any> = {};
export let initialData: Record<string, any> = {};
export let context: 'create' | 'edit' = 'create';
</script>

<AutocompleteSelect
{form}
options={getOptions({ objects: model.foreignKeys['ro_to_couple'], label: 'str' })}
field="ro_to_couple"
cacheLock={cacheLocks['ro_to_couple']}
bind:cachedValue={formDataCache['ro_to_couple']}
label={m.roToCouple()}
hidden={initialData.ro_to_couple}
/>
<AutocompleteSelect
{form}
multiple
options={getOptions({
objects: model.foreignKeys['stakeholders'],
label: 'str'
})}
field="stakeholders"
cacheLock={cacheLocks['stakeholders']}
bind:cachedValue={formDataCache['stakeholders']}
label={m.stakeholders()}
/>

<Checkbox {form} field="is_selected" label={m.selected()} />
<TextArea
{form}
field="justification"
label={m.justification()}
cacheLock={cacheLocks['justification']}
bind:cachedValue={formDataCache['justification']}
/>
116 changes: 116 additions & 0 deletions frontend/src/lib/components/Forms/ModelForm/StakeholderForm.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<script lang="ts">
import type { SuperValidated } from 'sveltekit-superforms';
import type { ModelInfo, CacheLock } from '$lib/utils/types';
import AutocompleteSelect from '$lib/components/Forms/AutocompleteSelect.svelte';
import Select from '$lib/components/Forms/Select.svelte';
import * as m from '$paraglide/messages.js';
import { getOptions } from '$lib/utils/crud';
import TextArea from '../TextArea.svelte';
import NumberField from '../NumberField.svelte';
import Checkbox from '../Checkbox.svelte';
export let form: SuperValidated<any>;
export let model: ModelInfo;
export let cacheLocks: Record<string, CacheLock> = {};
export let formDataCache: Record<string, any> = {};
export let initialData: Record<string, any> = {};
export let context: 'create' | 'edit' = 'create';
</script>

<AutocompleteSelect
{form}
options={getOptions({ objects: model.foreignKeys['entity'] })}
field="entity"
cacheLock={cacheLocks['entity']}
bind:cachedValue={formDataCache['entity']}
label={m.entity()}
hidden={initialData.entity}
/>
<Select
{form}
options={model.selectOptions['category']}
field="category"
label={m.category()}
cacheLock={cacheLocks['category']}
bind:cachedValue={formDataCache['category']}
/>

{#if context === 'edit'}
<NumberField
{form}
field="current_dependency"
label={m.currentDependency()}
cacheLock={cacheLocks['current_dependency']}
bind:cachedValue={formDataCache['current_dependency']}
/>
<NumberField
{form}
field="current_penetration"
label={m.currentPenetration()}
cacheLock={cacheLocks['current_penetration']}
bind:cachedValue={formDataCache['current_penetration']}
/>
<NumberField
{form}
field="current_maturity"
label={m.currentMaturity()}
cacheLock={cacheLocks['current_maturity']}
bind:cachedValue={formDataCache['current_maturity']}
/>
<NumberField
{form}
field="current_trust"
label={m.currentTrust()}
cacheLock={cacheLocks['current_trust']}
bind:cachedValue={formDataCache['current_trust']}
/>

<AutocompleteSelect
multiple
{form}
options={getOptions({
objects: model.foreignKeys['applied_controls'],
extra_fields: [['folder', 'str']]
})}
field="applied_controls"
label={m.appliedControls()}
/>

<NumberField
{form}
field="residual_dependency"
label={m.residualDependency()}
cacheLock={cacheLocks['residual_dependency']}
bind:cachedValue={formDataCache['residual_dependency']}
/>
<NumberField
{form}
field="residual_penetration"
label={m.residualPenetration()}
cacheLock={cacheLocks['residual_penetration']}
bind:cachedValue={formDataCache['residual_penetration']}
/>
<NumberField
{form}
field="residual_maturity"
label={m.residualMaturity()}
cacheLock={cacheLocks['residual_maturity']}
bind:cachedValue={formDataCache['residual_maturity']}
/>
<NumberField
{form}
field="residual_trust"
label={m.residualTrust()}
cacheLock={cacheLocks['residual_trust']}
bind:cachedValue={formDataCache['residual_trust']}
/>

<Checkbox {form} field="is_selected" label={m.selected()} />
<TextArea
{form}
field="justification"
label={m.justification()}
cacheLock={cacheLocks['justification']}
bind:cachedValue={formDataCache['justification']}
/>
{/if}
36 changes: 33 additions & 3 deletions frontend/src/lib/utils/crud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export const getOptions = ({
interface ForeignKeyField {
field: string;
urlModel: urlModel;
endpointUrl?: string;
urlParams?: string;
}

Expand Down Expand Up @@ -618,7 +619,7 @@ export const URL_MODEL_MAP: ModelMap = {
verboseName: 'Feared event',
verboseNamePlural: 'Feared events',
foreignKeyFields: [
{ field: 'ebios_rm_study', urlModel: 'ebios-rm' },
{ field: 'ebios_rm_study', urlModel: 'ebios-rm', endpointUrl: 'ebios-rm/studies' },
{ field: 'assets', urlModel: 'assets' },
{ field: 'qualifications', urlModel: 'qualifications' }
],
Expand All @@ -632,15 +633,44 @@ export const URL_MODEL_MAP: ModelMap = {
verboseName: 'Ro to',
verboseNamePlural: 'Ro to',
foreignKeyFields: [
{ field: 'ebios_rm_study', urlModel: 'ebios-rm' },
{ field: 'feared_events', urlModel: 'feared-events' }
{ field: 'ebios_rm_study', urlModel: 'ebios-rm', endpointUrl: 'ebios-rm/studies' },
{ field: 'feared_events', urlModel: 'feared-events', endpointUrl: 'ebios-rm/feared-events' }
],
selectFields: [
{ field: 'risk-origin' },
{ field: 'motivation', valueType: 'number' },
{ field: 'resources', valueType: 'number' },
{ field: 'pertinence', valueType: 'number' }
]
},
stakeholders: {
endpointUrl: 'ebios-rm/stakeholders',
name: 'stakeholder',
localName: 'stakeholder',
localNamePlural: 'stakeholders',
verboseName: 'Stakeholder',
verboseNamePlural: 'Stakeholders',
foreignKeyFields: [
{ field: 'entity', urlModel: 'entities' },
{ field: 'applied_controls', urlModel: 'applied-controls' },
{ field: 'ebios_rm_study', urlModel: 'ebios-rm', endpointUrl: 'ebios-rm/studies' },
{ field: 'folder', urlModel: 'folders', urlParams: 'content_type=DO' }
],
selectFields: [{ field: 'category' }]
},
'attack-paths': {
endpointUrl: 'ebios-rm/attack-paths',
name: 'attackpath',
localName: 'attackPath',
localNamePlural: 'attackPaths',
verboseName: 'Attack path',
verboseNamePlural: 'Attack paths',
foreignKeyFields: [
{ field: 'stakeholders', urlModel: 'stakeholders', endpointUrl: 'ebios-rm/stakeholders' },
{ field: 'ro_to_couple', urlModel: 'ro-to', endpointUrl: 'ebios-rm/ro-to' },
{ field: 'ebios_rm_study', urlModel: 'ebios-rm', endpointUrl: 'ebios-rm/studies' },
{ field: 'folder', urlModel: 'folders', urlParams: 'content_type=DO' }
]
}
};

Expand Down
30 changes: 29 additions & 1 deletion frontend/src/lib/utils/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,32 @@ export const roToSchema = z.object({
justification: z.string().optional()
});

export const StakeholderSchema = z.object({
ebios_rm_study: z.string(),
applied_controls: z.string().uuid().optional().array().optional(),
category: z.string().optional(),
entity: z.string().optional(),
current_dependency: z.number().min(0).max(4).optional(),
current_penetration: z.number().min(0).max(4).optional(),
current_maturity: z.number().min(1).max(4).optional(),
current_trust: z.number().min(1).max(4).optional(),
residual_dependency: z.number().min(0).max(4).optional(),
residual_penetration: z.number().min(0).max(4).optional(),
residual_maturity: z.number().min(1).max(4).optional(),
residual_trust: z.number().min(1).max(4).optional(),
is_selected: z.boolean().optional(),
justification: z.string().optional()
});

export const AttackPathSchema = z.object({
ebios_rm_study: z.string(),
ro_to_couple: z.string().uuid(),
stakeholders: z.string().uuid().array(),
description: z.string(),
is_selected: z.boolean().optional(),
justification: z.string().optional()
});

const SCHEMA_MAP: Record<string, AnyZodObject> = {
folders: FolderSchema,
projects: ProjectSchema,
Expand Down Expand Up @@ -453,7 +479,9 @@ const SCHEMA_MAP: Record<string, AnyZodObject> = {
'filtering-labels': FilteringLabelSchema,
'ebios-rm': ebiosRMSchema,
'feared-events': fearedEventsSchema,
'ro-to': roToSchema
'ro-to': roToSchema,
stakeholders: StakeholderSchema,
'attack-paths': AttackPathSchema
};

export const modelSchema = (model: string) => {
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/lib/utils/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -591,5 +591,13 @@ export const listViewFields: ListViewFieldsConfig = {
'feared_events',
'pertinence'
]
},
stakeholders: {
head: ['entity', 'category', 'current_criticality', 'applied_controls', 'residual_criticality'],
body: ['entity', 'category', 'current_criticality', 'applied_controls', 'residual_criticality']
},
'attack-paths': {
head: ['risk_origin', 'target_objective', 'stakeholders', 'attackPath'],
body: ['risk_origin', 'target_objective', 'stakeholders', 'description']
}
};
Loading

0 comments on commit 020b34a

Please sign in to comment.