From aef8ff09e7159f746003b78e27033eacbda0d38d Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 6 Nov 2023 09:46:08 -0500 Subject: [PATCH] Add feature-specific object type validation --- netbox/core/models/jobs.py | 10 ++++++++++ netbox/extras/models/change_logging.py | 13 +++++++++++++ netbox/extras/models/models.py | 23 ++++++++++++++++++++--- netbox/tenancy/models/contacts.py | 11 +++++++++++ 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/netbox/core/models/jobs.py b/netbox/core/models/jobs.py index f2151da92..5b9b41e53 100644 --- a/netbox/core/models/jobs.py +++ b/netbox/core/models/jobs.py @@ -3,6 +3,7 @@ import uuid import django_rq from django.conf import settings from django.contrib.contenttypes.fields import GenericForeignKey +from django.core.exceptions import ValidationError from django.core.validators import MinValueValidator from django.db import models from django.urls import reverse @@ -121,6 +122,15 @@ class Job(models.Model): def get_status_color(self): return JobStatusChoices.colors.get(self.status) + def clean(self): + super().clean() + + # Validate the assigned object type + if self.object_type not in ContentType.objects.with_feature('jobs'): + raise ValidationError( + _("Jobs cannot be assigned to this object type ({type}).").format(type=self.object_type) + ) + @property def duration(self): if not self.completed: diff --git a/netbox/extras/models/change_logging.py b/netbox/extras/models/change_logging.py index 072187e6f..5db0bba57 100644 --- a/netbox/extras/models/change_logging.py +++ b/netbox/extras/models/change_logging.py @@ -1,9 +1,11 @@ from django.conf import settings from django.contrib.contenttypes.fields import GenericForeignKey +from django.core.exceptions import ValidationError from django.db import models from django.urls import reverse from django.utils.translation import gettext_lazy as _ +from core.models import ContentType from extras.choices import * from ..querysets import ObjectChangeQuerySet @@ -103,6 +105,17 @@ class ObjectChange(models.Model): self.user_name ) + def clean(self): + super().clean() + + # Validate the assigned object type + if self.changed_object_type not in ContentType.objects.with_feature('change_logging'): + raise ValidationError( + _("Change logging is not supported for this object type ({type}).").format( + type=self.changed_object_type + ) + ) + def save(self, *args, **kwargs): # Record the user's name and the object's representation as static strings diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index 1de8e4def..67b455ab4 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -559,6 +559,15 @@ class ImageAttachment(ChangeLoggedModel): filename = self.image.name.rsplit('/', 1)[-1] return filename.split('_', 2)[2] + def clean(self): + super().clean() + + # Validate the assigned object type + if self.content_type not in ContentType.objects.with_feature('image_attachments'): + raise ValidationError( + _("Image attachments cannot be assigned to this object type ({type}).").format(type=self.content_type) + ) + def delete(self, *args, **kwargs): _name = self.image.name @@ -643,9 +652,8 @@ class JournalEntry(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ExportTemplat def clean(self): super().clean() - # Prevent the creation of journal entries on unsupported models - permitted_types = ContentType.objects.with_feature('journaling') - if self.assigned_object_type not in permitted_types: + # Validate the assigned object type + if self.assigned_object_type not in ContentType.objects.with_feature('journaling'): raise ValidationError( _("Journaling is not supported for this object type ({type}).").format(type=self.assigned_object_type) ) @@ -694,6 +702,15 @@ class Bookmark(models.Model): return str(self.object) return super().__str__() + def clean(self): + super().clean() + + # Validate the assigned object type + if self.object_type not in ContentType.objects.with_feature('bookmarks'): + raise ValidationError( + _("Bookmarks cannot be assigned to this object type ({type}).").format(type=self.object_type) + ) + class ConfigRevision(models.Model): """ diff --git a/netbox/tenancy/models/contacts.py b/netbox/tenancy/models/contacts.py index 306a1cad9..1b2e23f53 100644 --- a/netbox/tenancy/models/contacts.py +++ b/netbox/tenancy/models/contacts.py @@ -1,8 +1,10 @@ from django.contrib.contenttypes.fields import GenericForeignKey +from django.core.exceptions import ValidationError from django.db import models from django.urls import reverse from django.utils.translation import gettext_lazy as _ +from core.models import ContentType from netbox.models import ChangeLoggedModel, NestedGroupModel, OrganizationalModel, PrimaryModel from netbox.models.features import TagsMixin from tenancy.choices import * @@ -156,6 +158,15 @@ class ContactAssignment(ChangeLoggedModel, TagsMixin): def get_absolute_url(self): return reverse('tenancy:contact', args=[self.contact.pk]) + def clean(self): + super().clean() + + # Validate the assigned object type + if self.content_type not in ContentType.objects.with_feature('contacts'): + raise ValidationError( + _("Contacts cannot be assigned to this object type ({type}).").format(type=self.content_type) + ) + def to_objectchange(self, action): objectchange = super().to_objectchange(action) objectchange.related_object = self.object