diff --git a/backend/core/migrations/0025_complianceassessment_folder_riskassessment_folder_and_more.py b/backend/core/migrations/0025_complianceassessment_folder_riskassessment_folder_and_more.py new file mode 100644 index 000000000..7a49eeda9 --- /dev/null +++ b/backend/core/migrations/0025_complianceassessment_folder_riskassessment_folder_and_more.py @@ -0,0 +1,187 @@ +# Generated by Django 5.1.1 on 2024-09-12 15:00 + +import django.db.models.deletion +import iam.models +from django.db import migrations, models + + +def set_assessment_folder(apps, schema_editor): + RiskAssessment = apps.get_model("core", "RiskAssessment") + ComplianceAssessment = apps.get_model("core", "ComplianceAssessment") + for risk_assessment in RiskAssessment.objects.all(): + risk_assessment.folder = risk_assessment.project.folder + risk_assessment.save() + for compliance_assessment in ComplianceAssessment.objects.all(): + compliance_assessment.folder = compliance_assessment.project.folder + compliance_assessment.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0024_appliedcontrol_owner"), + ("iam", "0006_alter_role_folder_alter_roleassignment_folder_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="complianceassessment", + name="folder", + field=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", + ), + ), + migrations.AddField( + model_name="riskassessment", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="appliedcontrol", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="asset", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="evidence", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="framework", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="loadedlibrary", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="project", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="referencecontrol", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="requirementassessment", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="requirementmappingset", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="requirementnode", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="riskacceptance", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="riskmatrix", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="storedlibrary", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="threat", + name="folder", + field=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", + ), + ), + migrations.RunPython(set_assessment_folder), + ] diff --git a/backend/core/models.py b/backend/core/models.py index 64e3a6506..34c5356c1 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -31,7 +31,7 @@ from django.utils.html import format_html from django.utils.translation import get_language from django.utils.translation import gettext_lazy as _ -from iam.models import FolderMixin, PublishInRootFolderMixin +from iam.models import Folder, FolderMixin, PublishInRootFolderMixin from library.helpers import update_translations, update_translations_in_object from structlog import get_logger @@ -1364,7 +1364,7 @@ def save(self, *args, **kwargs): ########################### Secondary objects ######################### -class Assessment(NameDescriptionMixin, ETADueDateMixin): +class Assessment(NameDescriptionMixin, ETADueDateMixin, FolderMixin): class Status(models.TextChoices): PLANNED = "planned", _("Planned") IN_PROGRESS = "in_progress", _("In progress") @@ -1409,6 +1409,11 @@ class Status(models.TextChoices): class Meta: abstract = True + def save(self, *args, **kwargs) -> None: + if not self.folder or self.folder == Folder.get_root_folder(): + self.folder = self.project.folder + return super().save(*args, **kwargs) + class RiskAssessment(Assessment): risk_matrix = models.ForeignKey( diff --git a/backend/core/serializers.py b/backend/core/serializers.py index 74ffd00a2..baaf1456e 100644 --- a/backend/core/serializers.py +++ b/backend/core/serializers.py @@ -188,6 +188,7 @@ class Meta: class RiskAssessmentReadSerializer(AssessmentReadSerializer): str = serializers.CharField(source="__str__") project = FieldsRelatedField(["id", "folder"]) + folder = FieldsRelatedField() risk_scenarios = FieldsRelatedField(many=True) risk_scenarios_count = serializers.IntegerField(source="risk_scenarios.count") risk_matrix = FieldsRelatedField() @@ -535,6 +536,7 @@ class Meta: class ComplianceAssessmentReadSerializer(AssessmentReadSerializer): project = FieldsRelatedField(["id", "folder"]) + folder = FieldsRelatedField() framework = FieldsRelatedField( ["id", "min_score", "max_score", "implementation_groups_definition", "ref_id"] ) diff --git a/backend/global_settings/migrations/0002_alter_globalsettings_folder.py b/backend/global_settings/migrations/0002_alter_globalsettings_folder.py new file mode 100644 index 000000000..c5c6fd351 --- /dev/null +++ b/backend/global_settings/migrations/0002_alter_globalsettings_folder.py @@ -0,0 +1,25 @@ +# Generated by Django 5.1.1 on 2024-09-12 15:00 + +import django.db.models.deletion +import iam.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("global_settings", "0001_initial"), + ("iam", "0006_alter_role_folder_alter_roleassignment_folder_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="globalsettings", + name="folder", + field=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", + ), + ), + ] diff --git a/backend/iam/migrations/0006_alter_role_folder_alter_roleassignment_folder_and_more.py b/backend/iam/migrations/0006_alter_role_folder_alter_roleassignment_folder_and_more.py new file mode 100644 index 000000000..9226e38b4 --- /dev/null +++ b/backend/iam/migrations/0006_alter_role_folder_alter_roleassignment_folder_and_more.py @@ -0,0 +1,54 @@ +# Generated by Django 5.1.1 on 2024-09-12 15:00 + +import django.db.models.deletion +import iam.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("iam", "0005_alter_user_managers"), + ] + + operations = [ + migrations.AlterField( + model_name="role", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="roleassignment", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="user", + name="folder", + field=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", + ), + ), + migrations.AlterField( + model_name="usergroup", + name="folder", + field=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", + ), + ), + ] diff --git a/backend/iam/models.py b/backend/iam/models.py index 252cf1000..bb3d04abc 100644 --- a/backend/iam/models.py +++ b/backend/iam/models.py @@ -61,6 +61,14 @@ def get_root_folder() -> Self: """class function for general use""" return _get_root_folder() + @staticmethod + def get_root_folder_id() -> uuid.UUID: + """class function for general use""" + try: + return uuid.UUID(_get_root_folder().id) + except: + return _get_root_folder() + class ContentType(models.TextChoices): """content type for a folder""" @@ -173,7 +181,7 @@ class FolderMixin(models.Model): Folder, on_delete=models.CASCADE, related_name="%(class)s_folder", - default=Folder.get_root_folder, + default=Folder.get_root_folder_id, ) class Meta: @@ -382,10 +390,7 @@ def get_full_name(self) -> str: def get_short_name(self) -> str: """get user's short name (i.e. first_name or email before @))""" - try: - return self.first_name if self.first_name else self.email.split("@")[0] - except: - return "" + return self.first_name if self.first_name else self.email.split("@")[0] def mailing(self, email_template_name, subject, pk=False): """ diff --git a/frontend/src/lib/utils/crud.ts b/frontend/src/lib/utils/crud.ts index da9cf3434..7e41aba00 100644 --- a/frontend/src/lib/utils/crud.ts +++ b/frontend/src/lib/utils/crud.ts @@ -175,6 +175,7 @@ export const URL_MODEL_MAP: ModelMap = { verboseName: 'Risk assessment', verboseNamePlural: 'Risk assessments', foreignKeyFields: [ + { field: 'folder', urlModel: 'folders', urlParams: 'content_type=DO' }, { field: 'project', urlModel: 'projects' }, { field: 'authors', urlModel: 'users' }, { field: 'reviewers', urlModel: 'users' }, @@ -387,6 +388,7 @@ export const URL_MODEL_MAP: ModelMap = { verboseName: 'Compliance assessment', verboseNamePlural: 'Compliance assessments', foreignKeyFields: [ + { field: 'folder', urlModel: 'folders', urlParams: 'content_type=DO' }, { field: 'project', urlModel: 'projects' }, { field: 'framework', urlModel: 'frameworks' }, { field: 'authors', urlModel: 'users' },