From 4f3dc454a9bc8f2d6c6dc4d00f9b53671b939567 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Thu, 14 Mar 2024 11:00:51 +0100 Subject: [PATCH 1/5] Filter folders' queryset to domains --- backend/core/views.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/core/views.py b/backend/core/views.py index 64c006fe7..c41a58c54 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -863,6 +863,9 @@ class FolderViewSet(BaseModelViewSet): model = Folder filterset_fields = ["parent_folder", "content_type"] + def get_queryset(self): + return Folder.objects.filter(content_type=Folder.ContentType.DOMAIN) + def perform_create(self, serializer): """ Create the default user groups after domain creation @@ -887,7 +890,7 @@ def perform_create(self, serializer): role=Role.objects.get(name=RoleCodename.AUDITOR), builtin=True, folder=Folder.get_root_folder(), - is_recursive=True + is_recursive=True, ) ra1.perimeter_folders.add(folder) ra2 = RoleAssignment.objects.create( @@ -895,7 +898,7 @@ def perform_create(self, serializer): role=Role.objects.get(name=RoleCodename.APPROVER), builtin=True, folder=Folder.get_root_folder(), - is_recursive=True + is_recursive=True, ) ra2.perimeter_folders.add(folder) ra3 = RoleAssignment.objects.create( @@ -903,7 +906,7 @@ def perform_create(self, serializer): role=Role.objects.get(name=RoleCodename.ANALYST), builtin=True, folder=Folder.get_root_folder(), - is_recursive=True + is_recursive=True, ) ra3.perimeter_folders.add(folder) ra4 = RoleAssignment.objects.create( @@ -911,7 +914,7 @@ def perform_create(self, serializer): role=Role.objects.get(name=RoleCodename.DOMAIN_MANAGER), builtin=True, folder=Folder.get_root_folder(), - is_recursive=True + is_recursive=True, ) ra4.perimeter_folders.add(folder) From 6a2cc35420d2bd4cf7930ea17f7ea5cd68042997 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Thu, 14 Mar 2024 15:59:35 +0100 Subject: [PATCH 2/5] Fix domain queryset --- backend/core/views.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/core/views.py b/backend/core/views.py index c41a58c54..86fb1bbd4 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -864,7 +864,10 @@ class FolderViewSet(BaseModelViewSet): filterset_fields = ["parent_folder", "content_type"] def get_queryset(self): - return Folder.objects.filter(content_type=Folder.ContentType.DOMAIN) + queryset = super().get_queryset() + if not queryset: + return queryset + return queryset.filter(content_type=Folder.ContentType.DOMAIN) def perform_create(self, serializer): """ From b95e416f20b1f5c646abb323eebcfd9cbbfd8ba1 Mon Sep 17 00:00:00 2001 From: eric-intuitem <71850047+eric-intuitem@users.noreply.github.com> Date: Thu, 14 Mar 2024 19:29:38 +0100 Subject: [PATCH 3/5] Update data-model.md --- documentation/architecture/data-model.md | 31 ++++++++++++------------ 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/documentation/architecture/data-model.md b/documentation/architecture/data-model.md index 18059f91c..b5f2adb38 100644 --- a/documentation/architecture/data-model.md +++ b/documentation/architecture/data-model.md @@ -29,21 +29,22 @@ erDiagram ```mermaid erDiagram - ROOT_FOLDER ||--o{ DOMAIN : contains - DOMAIN ||--o{ PROJECT : contains - DOMAIN ||--o{ APPLIED_CONTROL : contains - ROOT_FOLDER ||--o{ THREAT : contains - DOMAIN ||--o{ RISK_ACCEPTANCE : contains - DOMAIN ||--o{ RISK_ASSESSMENT_REVIEW : contains - DOMAIN ||--o{ COMPLIANCE_ASSESSMENT_REVIEW: contains - DOMAIN ||--o{ EVIDENCE : contains - ROOT_FOLDER ||--o{ FRAMEWORK : contains - ROOT_FOLDER ||--o{ REFERENCE_CONTROL : contains - ROOT_FOLDER ||--o{ 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 ||--o{ DOMAIN : contains + DOMAIN ||--o{ PROJECT : contains + DOMAIN ||--o{ APPLIED_CONTROL : contains + DOMAIN ||--o{ RISK_ACCEPTANCE : contains + DOMAIN ||--o{ RISK_ASSESSMENT_REVIEW : contains + DOMAIN ||--o{ COMPLIANCE_ASSESSMENT_REVIEW: contains + DOMAIN ||--o{ EVIDENCE : contains + ROOT_FOLDER ||--o{ FRAMEWORK : contains + ROOT_FOLDER ||--o{ REFERENCE_CONTROL : contains + ROOT_FOLDER ||--o{ 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{ ASSET : contains + ROOT_FOLDER_OR_DOMAIN ||--o{ THREAT : contains DOMAIN { string name From 469bdcfdc51487f0d58a8892628cc52f47354bff Mon Sep 17 00:00:00 2001 From: eric-intuitem <71850047+eric-intuitem@users.noreply.github.com> Date: Fri, 15 Mar 2024 15:37:27 +0100 Subject: [PATCH 4/5] Update data-model.md clarify global domain handling --- documentation/architecture/data-model.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/architecture/data-model.md b/documentation/architecture/data-model.md index b5f2adb38..8bf501dfb 100644 --- a/documentation/architecture/data-model.md +++ b/documentation/architecture/data-model.md @@ -31,11 +31,8 @@ erDiagram ROOT_FOLDER ||--o{ DOMAIN : contains DOMAIN ||--o{ PROJECT : contains - DOMAIN ||--o{ APPLIED_CONTROL : contains - DOMAIN ||--o{ RISK_ACCEPTANCE : contains DOMAIN ||--o{ RISK_ASSESSMENT_REVIEW : contains DOMAIN ||--o{ COMPLIANCE_ASSESSMENT_REVIEW: contains - DOMAIN ||--o{ EVIDENCE : contains ROOT_FOLDER ||--o{ FRAMEWORK : contains ROOT_FOLDER ||--o{ REFERENCE_CONTROL : contains ROOT_FOLDER ||--o{ LIBRARY : contains @@ -43,6 +40,9 @@ erDiagram 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{ APPLIED_CONTROL : contains + ROOT_FOLDER_OR_DOMAIN ||--o{ RISK_ACCEPTANCE : contains ROOT_FOLDER_OR_DOMAIN ||--o{ ASSET : contains ROOT_FOLDER_OR_DOMAIN ||--o{ THREAT : contains From ec44a3b12a0f41c09a4c3480bf4e2d4c83db80ca Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Fri, 15 Mar 2024 21:11:35 +0100 Subject: [PATCH 5/5] Publish assets, threats, risk acceptances, applied controls, evidences when attached to root folder --- backend/core/models.py | 15 +++++++-------- backend/core/views.py | 6 ------ backend/iam/models.py | 25 +++++++++++++++++++++++-- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/backend/core/models.py b/backend/core/models.py index 9b1a104f2..08be7cbfd 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -9,7 +9,7 @@ from .base_models import * from .validators import validate_file_size, validate_file_name from .utils import camel_case -from iam.models import FolderMixin +from iam.models import FolderMixin, PublishInRootFolderMixin from django.core import serializers import os @@ -162,7 +162,7 @@ def delete(self, *args, **kwargs): super(Library, self).delete(*args, **kwargs) -class Threat(ReferentialObjectMixin): +class Threat(ReferentialObjectMixin, PublishInRootFolderMixin): library = models.ForeignKey( Library, on_delete=models.CASCADE, null=True, blank=True, related_name="threats" ) @@ -484,7 +484,7 @@ def __str__(self): return self.name -class Asset(NameDescriptionMixin, FolderMixin): +class Asset(NameDescriptionMixin, FolderMixin, PublishInRootFolderMixin): class Type(models.TextChoices): """ The type of the asset. @@ -535,7 +535,7 @@ def ancestors_plus_self(self) -> list[Self]: return list(result) -class Evidence(NameDescriptionMixin, FolderMixin): +class Evidence(NameDescriptionMixin, FolderMixin, PublishInRootFolderMixin): # TODO: Manage file upload to S3/MiniO attachment = models.FileField( # upload_to=settings.LOCAL_STORAGE_DIRECTORY, @@ -570,7 +570,7 @@ def filename(self): return os.path.basename(self.attachment.name) -class AppliedControl(NameDescriptionMixin, FolderMixin): +class AppliedControl(NameDescriptionMixin, FolderMixin, PublishInRootFolderMixin): class Status(models.TextChoices): PLANNED = "planned", _("Planned") ACTIVE = "active", _("Active") @@ -757,7 +757,7 @@ class Status(models.TextChoices): default=Status.PLANNED, verbose_name=_("Status"), blank=True, - null=True + null=True, ) authors = models.ManyToManyField( User, @@ -1284,7 +1284,6 @@ class Result(models.TextChoices): verbose_name=_("Result"), ) - class Meta: verbose_name = _("Compliance assessment") verbose_name_plural = _("Compliance assessments") @@ -1513,7 +1512,7 @@ class Meta: ########################### RiskAcesptance is a domain object relying on secondary objects ######################### -class RiskAcceptance(NameDescriptionMixin, FolderMixin): +class RiskAcceptance(NameDescriptionMixin, FolderMixin, PublishInRootFolderMixin): ACCEPTANCE_STATE = [ ("created", _("Created")), ("submitted", _("Submitted")), diff --git a/backend/core/views.py b/backend/core/views.py index 86fb1bbd4..946f52732 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -863,12 +863,6 @@ class FolderViewSet(BaseModelViewSet): model = Folder filterset_fields = ["parent_folder", "content_type"] - def get_queryset(self): - queryset = super().get_queryset() - if not queryset: - return queryset - return queryset.filter(content_type=Folder.ContentType.DOMAIN) - def perform_create(self, serializer): """ Create the default user groups after domain creation diff --git a/backend/iam/models.py b/backend/iam/models.py index 86db1652e..3461fc726 100644 --- a/backend/iam/models.py +++ b/backend/iam/models.py @@ -183,6 +183,24 @@ class Meta: abstract = True +class PublishInRootFolderMixin(models.Model): + """ + Set is_published to True if object is attached to the root folder + """ + + class Meta: + abstract = True + + def save(self, *args, **kwargs): + if ( + getattr(self, "folder") == Folder.get_root_folder() + and hasattr(self, "is_published") + and not self.is_published + ): + self.is_published = True + super().save(*args, **kwargs) + + class UserGroup(NameDescriptionMixin, FolderMixin): """UserGroup objects contain users and can be used as principals in role assignments""" @@ -201,7 +219,7 @@ def __str__(self) -> str: def get_name_display(self) -> str: return self.name - + def get_localization_dict(self) -> dict: return { "folder": self.folder.name, @@ -509,7 +527,10 @@ def is_access_allowed( for ra in RoleAssignment.get_role_assignments(user): f = folder while f is not None: - if f in ra.perimeter_folders.all() and perm in ra.role.permissions.all(): + if ( + f in ra.perimeter_folders.all() + and perm in ra.role.permissions.all() + ): return True f = f.parent_folder return False