From 3b0b85b34b59a6c771181353c9994dc1aec0340c Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Tue, 1 Oct 2024 12:27:29 +0200 Subject: [PATCH] feat: add representatives field in entity assessment --- .../0003_entityassessment_representatives.py | 24 +++++++++ backend/tprm/models.py | 6 +++ backend/tprm/serializers.py | 54 ++++++++++++------- .../ModelForm/EntityAssessmentForm.svelte | 11 +++- frontend/src/lib/utils/crud.ts | 3 +- frontend/src/lib/utils/schemas.ts | 3 +- 6 files changed, 78 insertions(+), 23 deletions(-) create mode 100644 backend/tprm/migrations/0003_entityassessment_representatives.py diff --git a/backend/tprm/migrations/0003_entityassessment_representatives.py b/backend/tprm/migrations/0003_entityassessment_representatives.py new file mode 100644 index 000000000..66d0fb8e1 --- /dev/null +++ b/backend/tprm/migrations/0003_entityassessment_representatives.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1 on 2024-10-01 09:14 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("tprm", "0002_alter_entity_reference_link"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name="entityassessment", + name="representatives", + field=models.ManyToManyField( + blank=True, + related_name="entity_assessments", + to=settings.AUTH_USER_MODEL, + verbose_name="Representative", + ), + ), + ] diff --git a/backend/tprm/models.py b/backend/tprm/models.py index 6fe6815bd..9a5063f65 100644 --- a/backend/tprm/models.py +++ b/backend/tprm/models.py @@ -40,6 +40,12 @@ class Conclusion(models.TextChoices): dependency = models.IntegerField(default=0, verbose_name=_("Dependency")) maturity = models.IntegerField(default=0, verbose_name=_("Maturity")) trust = models.IntegerField(default=0, verbose_name=_("Trust")) + representatives = models.ManyToManyField( + User, + blank=True, + verbose_name=_("Representative"), + related_name="entity_assessments", + ) entity = models.ForeignKey( Entity, on_delete=models.CASCADE, diff --git a/backend/tprm/serializers.py b/backend/tprm/serializers.py index 398b8ab19..b5fd5dbda 100644 --- a/backend/tprm/serializers.py +++ b/backend/tprm/serializers.py @@ -8,7 +8,6 @@ from django.contrib.auth import get_user_model from tprm.models import Entity, EntityAssessment, Representative, Solution from django.utils.translation import gettext_lazy as _ -from ciso_assistant.settings import EMAIL_HOST, EMAIL_HOST_RESCUE import structlog @@ -39,7 +38,7 @@ class EntityAssessmentReadSerializer(BaseModelSerializer): entity = FieldsRelatedField() folder = FieldsRelatedField() solutions = FieldsRelatedField(many=True) - authors = FieldsRelatedField(many=True) + representatives = FieldsRelatedField(many=True) reviewers = FieldsRelatedField(many=True) class Meta: @@ -48,7 +47,7 @@ class Meta: class EntityAssessmentWriteSerializer(BaseModelSerializer): - create_audit = serializers.BooleanField(default=True) + create_audit = serializers.BooleanField(default=False) framework = serializers.PrimaryKeyRelatedField( queryset=Framework.objects.all(), required=False ) @@ -82,23 +81,31 @@ def _create_or_update_audit(self, instance, audit_data): ], ) - if not instance.compliance_assessment: - enclave = Folder.objects.create( - content_type=Folder.ContentType.ENCLAVE, - name=f"{instance.project.name}/{instance.name}", - parent_folder=instance.folder, - ) - audit.folder = enclave - audit.save() + enclave = Folder.objects.create( + content_type=Folder.ContentType.ENCLAVE, + name=f"{instance.project.name}/{instance.name}", + parent_folder=instance.folder, + ) + audit.folder = enclave + audit.save() audit.create_requirement_assessments() - audit.authors.set(instance.authors.all()) audit.reviewers.set(instance.reviewers.all()) + audit.authors.set(instance.representatives.all()) instance.compliance_assessment = audit instance.save() + else: + if instance.compliance_assessment: + audit = instance.compliance_assessment + audit.reviewers.set(instance.reviewers.all()) + audit.authors.set(instance.representatives.all()) + instance.save() def _assign_third_party_respondents( - self, instance: EntityAssessment, third_party_users: set[User] + self, + instance: EntityAssessment, + third_party_users: set[User], + old_third_party_users: set[User] = set(), ): if instance.compliance_assessment: enclave = instance.compliance_assessment.folder @@ -119,25 +126,32 @@ def _assign_third_party_respondents( if not user.is_third_party: logger.warning("User is not a third-party", user=user) user.user_groups.add(respondents) + for user in old_third_party_users: + if not user.is_third_party: + logger.warning("User is not a third-party", user=user) + user.user_groups.remove(respondents) def create(self, validated_data): audit_data = self._extract_audit_data(validated_data) instance = super().create(validated_data) self._create_or_update_audit(instance, audit_data) - self._assign_third_party_respondents(instance, set(instance.authors.all())) + self._assign_third_party_respondents( + instance, set(instance.representatives.all()) + ) return instance def update(self, instance: EntityAssessment, validated_data): audit_data = self._extract_audit_data(validated_data) - new_authors = set(validated_data.get("authors", [])) - set( - instance.authors.all() + representatives = set(validated_data.get("representatives", [])) + old_representatives = set(instance.representatives.all()) - set( + validated_data.get("representatives", []) ) instance = super().update(instance, validated_data) - if not instance.compliance_assessment: - self._create_or_update_audit(instance, audit_data) - - self._assign_third_party_respondents(instance, new_authors) + self._create_or_update_audit(instance, audit_data) + self._assign_third_party_respondents( + instance, representatives, old_representatives + ) return instance class Meta: diff --git a/frontend/src/lib/components/Forms/ModelForm/EntityAssessmentForm.svelte b/frontend/src/lib/components/Forms/ModelForm/EntityAssessmentForm.svelte index c349a41ba..c49383b70 100644 --- a/frontend/src/lib/components/Forms/ModelForm/EntityAssessmentForm.svelte +++ b/frontend/src/lib/components/Forms/ModelForm/EntityAssessmentForm.svelte @@ -144,11 +144,20 @@ multiple options={getOptions({ objects: model.foreignKeys['authors'], label: 'email' })} field="authors" - helpText={m.entityAssessmentAuthorHelpText()} cacheLock={cacheLocks['authors']} bind:cachedValue={formDataCache['authors']} label={m.authors()} /> +