Skip to content

Commit

Permalink
Merge branch 'feat/tprm' of github.com:intuitem/ciso-assistant-commun…
Browse files Browse the repository at this point in the history
…ity into feat/tprm
  • Loading branch information
Mohamed-Hacene committed Sep 13, 2024
2 parents 2f3985f + 973574f commit 3bedd9e
Show file tree
Hide file tree
Showing 37 changed files with 268 additions and 126 deletions.
6 changes: 6 additions & 0 deletions backend/app_tests/api/test_api_applied_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
APPLIED_CONTROL_EFFORT2 = ("M", "Medium")
APPLIED_CONTROL_LINK = "https://example.com"
APPLIED_CONTROL_ETA = "2024-01-01"
APPLIED_CONTROL_COST = 24.42
APPLIED_CONTROL_COST2 = 25.43


@pytest.mark.django_db
Expand Down Expand Up @@ -104,6 +106,7 @@ def test_get_applied_controls(self, test):
"link": APPLIED_CONTROL_LINK,
"eta": APPLIED_CONTROL_ETA,
"effort": APPLIED_CONTROL_EFFORT[0],
"cost": APPLIED_CONTROL_COST,
"folder": test.folder,
},
{
Expand Down Expand Up @@ -135,6 +138,7 @@ def test_create_applied_controls(self, test):
"link": APPLIED_CONTROL_LINK,
"eta": APPLIED_CONTROL_ETA,
"effort": APPLIED_CONTROL_EFFORT[0],
"cost": APPLIED_CONTROL_COST,
"folder": str(test.folder.id),
},
{
Expand Down Expand Up @@ -167,6 +171,7 @@ def test_update_applied_controls(self, test):
"link": APPLIED_CONTROL_LINK,
"eta": APPLIED_CONTROL_ETA,
"effort": APPLIED_CONTROL_EFFORT[0],
"cost": APPLIED_CONTROL_COST,
"folder": test.folder,
},
{
Expand All @@ -177,6 +182,7 @@ def test_update_applied_controls(self, test):
"link": "new " + APPLIED_CONTROL_LINK,
"eta": "2025-01-01",
"effort": APPLIED_CONTROL_EFFORT2[0],
"cost": APPLIED_CONTROL_COST2,
"folder": str(folder.id),
},
{
Expand Down
21 changes: 21 additions & 0 deletions backend/core/migrations/0026_appliedcontrol_cost.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 5.1 on 2024-09-13 10:41

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("core", "0025_complianceassessment_folder_riskassessment_folder_and_more"),
]

operations = [
migrations.AddField(
model_name="appliedcontrol",
name="cost",
field=models.FloatField(
help_text="Cost of the measure (using globally-chosen currency)",
null=True,
verbose_name="Cost",
),
),
]
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Generated by Django 5.1.1 on 2024-09-13 15:42
# Generated by Django 5.1 on 2024-09-13 16:25

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('core', '0025_complianceassessment_folder_riskassessment_folder_and_more'),
('core', '0026_appliedcontrol_cost'),
]

operations = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Generated by Django 5.1 on 2024-09-13 18:31
# Generated by Django 5.1 on 2024-09-13 18:54

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('core', '0026_requirementassessment_answer_and_more'),
('core', '0027_requirementassessment_answer_and_more'),
]

operations = [
Expand Down
18 changes: 18 additions & 0 deletions backend/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1329,6 +1329,11 @@ class Status(models.TextChoices):
help_text=_("Relative effort of the measure (using T-Shirt sizing)"),
verbose_name=_("Effort"),
)
cost = models.FloatField(
null=True,
help_text=_("Cost of the measure (using globally-chosen currency)"),
verbose_name=_("Cost"),
)

fields_to_check = ["name"]

Expand Down Expand Up @@ -1689,6 +1694,19 @@ def quality_check(self) -> dict:
}
)

if not mtg["cost"]:
warnings_lst.append(
{
"msg": _(
"{} does not have an estimated cost. This will help you for prioritization"
).format(mtg["name"]),
"msgid": "appliedControlNoCost",
"link": f"applied-controls/{mtg['id']}",
"obj_type": "appliedcontrol",
"object": {"name": mtg["name"], "id": mtg["id"]},
}
)

if not mtg["link"]:
info_lst.append(
{
Expand Down
3 changes: 2 additions & 1 deletion backend/core/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def create(self, validated_data: Any):
return object_created
except Exception as e:
logger.error(e)
raise serializers.ValidationError(e.args[0])
raise serializers.ValidationError()

class Meta:
model: models.Model
Expand Down Expand Up @@ -311,6 +311,7 @@ class AppliedControlReadSerializer(AppliedControlWriteSerializer):
) # type : get_type_display
evidences = FieldsRelatedField(many=True)
effort = serializers.CharField(source="get_effort_display")
cost = serializers.FloatField()

ranking_score = serializers.IntegerField(source="get_ranking_score")
owner = FieldsRelatedField(many=True)
Expand Down
2 changes: 2 additions & 0 deletions backend/core/templates/core/action_plan_pdf.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ <h1 class="flex justify-center">{% trans "Action plan" %}</h1>
<th class="text-md p-2 text-center">{% trans "ETA" %}</th>
<th class="text-md p-2 text-center">{% trans "Expiry date" %}</th>
<th class="text-md p-2 text-center">{% trans "Effort" %}</th>
<th class="text-md p-2 text-center">{% trans "Cost" %}</th>
<th class="text-md p-2 text-center">{% trans "Matching requirements" %}</th>
</tr>
</thead>
Expand All @@ -59,6 +60,7 @@ <h1 class="flex justify-center">{% trans "Action plan" %}</h1>
<td class="text-md p-2 text-center">{{ applied_control.eta|default:"--" }}</td>
<td class="text-md p-2 text-center">{{ applied_control.expiry_date|default:"--" }}</td>
<td class="text-md p-2 text-center">{{ applied_control.get_effort_display|default:"--" }}</td>
<td class="text-md p-2 text-center">{{ applied_control.cost|default:"--" }}</td>
<td class="text-md p-2 text-center">{% get_requirements_count applied_control compliance_assessment %}</td>
</tr>
{% empty %}
Expand Down
2 changes: 2 additions & 0 deletions backend/core/templates/snippets/mp_data.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
<td class="px-2 font-semibold">{% trans "Reference control" %}</td>
<td class="px-2 font-semibold">{% trans "ETA" %}</td>
<td class="px-2 font-semibold">{% trans "Effort" %}</td>
<td class="px-2 font-semibold">{% trans "Cost" %}</td>
<td class="px-2 font-semibold text-center">{% trans "Link" %}</td>
<td class="px-2 font-semibold text-center">{% trans "Status" %}</td>
</tr>
Expand All @@ -74,6 +75,7 @@
<td class="px-2 py-3">{% if appliedcontrol.reference_control %}{{ appliedcontrol.reference_control }}{% else %}--{% endif %}</td>
<td class="px-2 py-3">{% if appliedcontrol.eta %}{{ appliedcontrol.eta }}{% else %}--{%endif%}</td>
<td class="px-2 py-3">{% if appliedcontrol.effort %}{{ appliedcontrol.effort }}{% else %}--{%endif%}</td>
<td class="px-2 py-3">{% if appliedcontrol.cost %}{{ appliedcontrol.cost }}{% else %}--{%endif%}</td>
<td class="px-2 py-3 text-center">{% if appliedcontrol.link %}<a onclick="event.stopPropagation();" href="{{ appliedcontrol.link }}"
class="hover:text-blue-400"><i
class="fas fa-external-link-square-alt"></i></a>{% else %}--{% endif %}
Expand Down
10 changes: 8 additions & 2 deletions backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ def treatment_plan_csv(self, request, pk):
"reference_control",
"eta",
"effort",
"cost",
"link",
"status",
]
Expand All @@ -457,6 +458,7 @@ def treatment_plan_csv(self, request, pk):
mtg.reference_control,
mtg.eta,
mtg.effort,
mtg.cost,
mtg.link,
mtg.status,
]
Expand Down Expand Up @@ -615,6 +617,7 @@ class AppliedControlViewSet(BaseModelViewSet):
"status",
"reference_control",
"effort",
"cost",
"risk_scenarios",
"requirement_assessments",
"evidences",
Expand Down Expand Up @@ -670,7 +673,7 @@ def todo(self, request):
"""measures = [{
key: getattr(mtg,key)
for key in [
"id","folder","reference_control","type","status","effort","name","description","eta","link","created_at","updated_at"
"id","folder","reference_control","type","status","effort", "cost", "name","description","eta","link","created_at","updated_at"
]
} for mtg in measures]
for i in range(len(measures)) :
Expand Down Expand Up @@ -1452,6 +1455,7 @@ def action_plan(self, request, pk):
"expiry_date": applied_control.expiry_date,
"link": applied_control.link,
"effort": applied_control.effort,
"cost": applied_control.cost,
"owners": [
{
"id": owner.id,
Expand Down Expand Up @@ -1781,7 +1785,7 @@ def todo(self, request):
"""measures = [{
key: getattr(mtg,key)
for key in [
"id","folder","reference_control","type","status","effort","name","description","eta","link","created_at","updated_at"
"id","folder","reference_control","type","status","effort","cost","name","description","eta","link","created_at","updated_at"
]
} for mtg in measures]
for i in range(len(measures)) :
Expand Down Expand Up @@ -1976,6 +1980,7 @@ def export_mp_csv(request):
"reference_control",
"eta",
"effort",
"cost",
"link",
"status",
]
Expand All @@ -1997,6 +2002,7 @@ def export_mp_csv(request):
mtg.reference_control,
mtg.eta,
mtg.effort,
mtg.cost,
mtg.link,
mtg.status,
]
Expand Down
18 changes: 18 additions & 0 deletions backend/iam/migrations/0008_user_is_third_party.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1 on 2024-09-13 17:33

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('iam', '0007_alter_folder_content_type'),
]

operations = [
migrations.AddField(
model_name='user',
name='is_third_party',
field=models.BooleanField(default=False),
),
]
1 change: 1 addition & 0 deletions backend/iam/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ class User(AbstractBaseUser, AbstractBaseModel, FolderMixin):
email = models.CharField(max_length=100, unique=True)
first_login = models.BooleanField(default=True)
is_sso = models.BooleanField(default=False)
is_third_party = models.BooleanField(default=False)
is_active = models.BooleanField(
_("active"),
default=True,
Expand Down
6 changes: 3 additions & 3 deletions backend/tprm/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 5.1 on 2024-09-13 18:31
# Generated by Django 5.1 on 2024-09-13 18:54

import django.db.models.deletion
import iam.models
Expand All @@ -12,8 +12,8 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
('core', '0027_complianceassessment_observation_and_more'),
('iam', '0007_alter_folder_content_type'),
('core', '0028_complianceassessment_observation_and_more'),
('iam', '0008_user_is_third_party'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

Expand Down
7 changes: 6 additions & 1 deletion documentation/architecture/data-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ erDiagram
DOMAIN ||--o{ RISK_ASSESSMENT_REVIEW : contains
DOMAIN ||--o{ COMPLIANCE_ASSESSMENT_REVIEW: contains
ROOT_FOLDER ||--o{ FRAMEWORK : contains
ROOT_FOLDER ||--o{ REFERENCE_CONTROL : contains
ROOT_FOLDER ||--o{ STORED_LIBRARY : contains
ROOT_FOLDER ||--o{ LOADED_LIBRARY : contains
ROOT_FOLDER ||--o{ USER : contains
ROOT_FOLDER ||--o{ USER_GROUP : contains
ROOT_FOLDER ||--o{ ROLE : contains
ROOT_FOLDER ||--o{ ROLE_ASSIGNMENT : contains
ROOT_FOLDER_OR_DOMAIN ||--o{ EVIDENCE : contains
ROOT_FOLDER_OR_DOMAIN ||--o{ REFERENCE_CONTROL : contains
ROOT_FOLDER_OR_DOMAIN ||--o{ APPLIED_CONTROL : contains
ROOT_FOLDER_OR_DOMAIN ||--o{ RISK_ACCEPTANCE : contains
ROOT_FOLDER_OR_DOMAIN ||--o{ ASSET : contains
Expand Down Expand Up @@ -232,6 +232,7 @@ erDiagram
date expiration
url link
string effort
float cost
string[] tags
}
Expand Down Expand Up @@ -603,6 +604,7 @@ namespace DomainObjects {
+DateField expiry_date
+CharField link
+CharField effort
+Decimal cost
+RiskScenario[] risk_scenarios()
+RiskAssessments[] risk_assessments()
Expand Down Expand Up @@ -785,11 +787,14 @@ A applied control has the following specific fields:
- an Estimated Time of Arrival date
- a validity date (expiration field)
- an effort (--/S/M/L/XL)
- a cost (--/float value)
- a url link
- a list of user-defined tags

When a applied control derives from a reference control, the same category and csf_function are proposed, but this can be changed.

Costs are measured in a global currency/multiple that is defined in global settings.

## Compliance and risk assessments

Both types of assessments have common fields:
Expand Down
8 changes: 8 additions & 0 deletions enterprise/frontend/src/lib/components/Forms/ModelForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,14 @@
cacheLock={cacheLocks['effort']}
bind:cachedValue={formDataCache['effort']}
/>
<Select
{form}
field="cost"
label={m.cost()}
helpText={m.costHelpText()}
cacheLock={cacheLocks['cost']}
bind:cachedValue={formDataCache['cost']}
/>
<AutocompleteSelect
{form}
options={getOptions({ objects: model.foreignKeys['folder'] })}
Expand Down
3 changes: 3 additions & 0 deletions frontend/messages/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
"rowCount": "عرض {start} إلى {end} من {total}",
"status": "الحالة",
"effort": "الجهد",
"cost": "يكلف",
"impact": "التأثير",
"expiryDate": "تاريخ الانتهاء",
"link": "الرابط",
Expand Down Expand Up @@ -411,6 +412,7 @@
"expiryDateHelpText": "التاريخ الذي لم يعد فيه الكائن صالحًا",
"linkHelpText": "عنوان URL خارجي لمتابعة الإجراءات (مثل تذكرة Jira)",
"effortHelpText": "الجهد المطلوب لتنفيذ التحكم المطبق",
"costHelpText": "التكلفة المطلوبة لتنفيذ الرقابة المطبقة",
"riskAcceptanceJusitficationHelpText": "تبرير قبول المخاطر. يمكن فقط للموافق تحرير هذا الحقل.",
"approverHelpText": "هوية مالك المخاطر والموافق",
"riskAcceptanceRiskScenariosHelpText": "سيناريوهات المخاطر التي تم قبولها",
Expand Down Expand Up @@ -558,6 +560,7 @@
"appliedControlNoETA": "لا يوجد وقت متوقع للوصول",
"appliedControlETAInPast": "الوقت المتوقع للوصول في الماضي. فكر في تحديث حالته أو تاريخه",
"appliedControlNoEffort": "لا يوجد جهد مقدر. سيساعدك هذا في تحديد الأولويات",
"appliedControlNoCost": "ليس لها تكلفة تقديرية. هذا سوف يساعدك على تحديد الأولويات",
"appliedControlNoLink": "التحكم المطبق لا يحتوي على رابط خارجي مرفق. سيساعدك هذا في متابعة الإجراءات",
"riskAcceptanceNoExpiryDate": "القبول لا يحتوي على تاريخ انتهاء الصلاحية",
"riskAcceptanceExpired": "القبول قد انتهت صلاحيته. فكر في تحديث حالته أو تاريخه",
Expand Down
3 changes: 3 additions & 0 deletions frontend/messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
"status": "Status",
"result": "Ergebnis",
"effort": "Aufwand",
"cost": "Kosten",
"impact": "Auswirkung",
"expiryDate": "Ablaufdatum",
"link": "Link",
Expand Down Expand Up @@ -424,6 +425,7 @@
"expiryDateHelpText": "Datum, ab dem das Objekt nicht mehr gültig ist",
"linkHelpText": "Externe URL zur Aktionsverfolgung (z. B. Jira-Ticket)",
"effortHelpText": "Der Aufwand, der für die Implementierung der angewendeten Kontrolle erforderlich ist",
"costHelpText": "Die für die Implementierung der angewandten Kontrolle erforderlichen Kosten",
"riskAcceptanceJusitficationHelpText": "Begründung für die Risikoakzeptanz. Nur der Genehmiger kann dieses Feld bearbeiten.",
"approverHelpText": "Identität des Risikoeigners und Genehmigers",
"riskAcceptanceRiskScenariosHelpText": "Die akzeptierten Risikoszenarien",
Expand Down Expand Up @@ -576,6 +578,7 @@
"appliedControlNoETA": "Hat keine ETA",
"appliedControlETAInPast": "Die voraussichtliche Ankunftszeit liegt nun in der Vergangenheit. Aktualisieren Sie den Status oder das Datum.",
"appliedControlNoEffort": "Hat keinen geschätzten Aufwand. Dies hilft Ihnen bei der Priorisierung",
"appliedControlNoCost": "Es gibt keinen geschätzten Kosten. Dies hilft Ihnen bei der Priorisierung",
"appliedControlNoLink": "Angewandte Kontrolle hat keinen externen Link angehängt. Dies hilft Ihnen bei der Nachverfolgung",
"riskAcceptanceNoExpiryDate": "Die Annahme hat kein Ablaufdatum",
"riskAcceptanceExpired": "Die Annahme ist abgelaufen. Erwägen Sie eine Aktualisierung des Status oder des Datums",
Expand Down
Loading

0 comments on commit 3bedd9e

Please sign in to comment.