Skip to content

Commit

Permalink
feat: add representatives field in entity assessment
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohamed-Hacene committed Oct 1, 2024
1 parent d68f8c3 commit 3b0b85b
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 23 deletions.
24 changes: 24 additions & 0 deletions backend/tprm/migrations/0003_entityassessment_representatives.py
Original file line number Diff line number Diff line change
@@ -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",
),
),
]
6 changes: 6 additions & 0 deletions backend/tprm/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
54 changes: 34 additions & 20 deletions backend/tprm/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand All @@ -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
)
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()}
/>
<AutocompleteSelect
{form}
multiple
options={getOptions({ objects: model.foreignKeys['representatives'], label: 'email' })}
field="representatives"
helpText={m.entityAssessmentAuthorHelpText()}
cacheLock={cacheLocks['representatives']}
bind:cachedValue={formDataCache['representatives']}
label={m.representatives()}
/>
<AutocompleteSelect
{form}
multiple
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lib/utils/crud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,8 @@ export const URL_MODEL_MAP: ModelMap = {
{ field: 'entity', urlModel: 'entities' },
{ field: 'solutions', urlModel: 'solutions' },
{ field: 'framework', urlModel: 'frameworks' },
{ field: 'authors', urlModel: 'users', urlParams: 'is_third_party=true' },
{ field: 'authors', urlModel: 'users', urlParams: 'is_third_party=false' },
{ field: 'representatives', urlModel: 'users', urlParams: 'is_third_party=true' },
{ field: 'reviewers', urlModel: 'users', urlParams: 'is_third_party=false' },
{ field: 'evidence', urlModel: 'evidences' },
{ field: 'compliance_assessment', urlModel: 'compliance-assessments' }
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lib/utils/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ export const EntitiesSchema = baseNamedObject({
});

export const EntityAssessmentSchema = baseNamedObject({
create_audit: z.boolean().optional().default(true),
create_audit: z.boolean().optional().default(false),
framework: z.string().optional(),
selected_implementation_groups: z.array(z.string().optional()).optional(),
version: z.string().optional().default('0.1'),
Expand All @@ -294,6 +294,7 @@ export const EntityAssessmentSchema = baseNamedObject({
eta: z.string().optional().nullable(),
due_date: z.string().optional().nullable(),
authors: z.array(z.string().optional()).optional(),
representatives: z.array(z.string().optional()).optional(),
reviewers: z.array(z.string().optional()).optional(),
entity: z.string(),
solutions: z.array(z.string().optional()).optional(),
Expand Down

0 comments on commit 3b0b85b

Please sign in to comment.