diff --git a/netbox/extras/constants.py b/netbox/extras/constants.py index 00a5c9979..7ca48f1d7 100644 --- a/netbox/extras/constants.py +++ b/netbox/extras/constants.py @@ -6,6 +6,7 @@ CUSTOMFIELD_MODELS = ( 'provider', 'circuit', # Circuits 'site', 'rack', 'devicetype', 'device', # DCIM 'aggregate', 'prefix', 'ipaddress', 'vlan', 'vrf', 'service', # IPAM + 'secret', # Secrets 'tenant', # Tenancy 'cluster', 'virtualmachine', # Virtualization ) diff --git a/netbox/secrets/api/serializers.py b/netbox/secrets/api/serializers.py index 0e24281bb..3c24edf2b 100644 --- a/netbox/secrets/api/serializers.py +++ b/netbox/secrets/api/serializers.py @@ -5,6 +5,7 @@ from rest_framework.validators import UniqueTogetherValidator from taggit.models import Tag from dcim.api.serializers import NestedDeviceSerializer +from extras.api.customfields import CustomFieldModelSerializer from secrets.models import Secret, SecretRole from utilities.api import TagField, ValidatedModelSerializer, WritableNestedSerializer @@ -32,7 +33,7 @@ class NestedSecretRoleSerializer(WritableNestedSerializer): # Secrets # -class SecretSerializer(ValidatedModelSerializer): +class SecretSerializer(CustomFieldModelSerializer): device = NestedDeviceSerializer() role = NestedSecretRoleSerializer() plaintext = serializers.CharField() @@ -40,7 +41,9 @@ class SecretSerializer(ValidatedModelSerializer): class Meta: model = Secret - fields = ['id', 'device', 'role', 'name', 'plaintext', 'hash', 'tags', 'created', 'last_updated'] + fields = [ + 'id', 'device', 'role', 'name', 'plaintext', 'hash', 'tags', 'custom_fields', 'created', 'last_updated', + ] validators = [] def validate(self, data): diff --git a/netbox/secrets/filters.py b/netbox/secrets/filters.py index 2499fa2bb..f43a82b22 100644 --- a/netbox/secrets/filters.py +++ b/netbox/secrets/filters.py @@ -4,6 +4,7 @@ import django_filters from django.db.models import Q from dcim.models import Device +from extras.filters import CustomFieldFilterSet from utilities.filters import NumericInFilter from .models import Secret, SecretRole @@ -15,7 +16,7 @@ class SecretRoleFilter(django_filters.FilterSet): fields = ['name', 'slug'] -class SecretFilter(django_filters.FilterSet): +class SecretFilter(CustomFieldFilterSet, django_filters.FilterSet): id__in = NumericInFilter(name='id', lookup_expr='in') q = django_filters.CharFilter( method='search', diff --git a/netbox/secrets/forms.py b/netbox/secrets/forms.py index 6961baf88..566abc0ee 100644 --- a/netbox/secrets/forms.py +++ b/netbox/secrets/forms.py @@ -7,8 +7,8 @@ from django.db.models import Count from taggit.forms import TagField from dcim.models import Device -from extras.forms import AddRemoveTagsForm -from utilities.forms import BootstrapMixin, BulkEditForm, FilterChoiceField, FlexibleModelChoiceField, SlugField +from extras.forms import AddRemoveTagsForm, CustomFieldBulkEditForm, CustomFieldFilterForm, CustomFieldForm +from utilities.forms import BootstrapMixin, FilterChoiceField, FlexibleModelChoiceField, SlugField from .models import Secret, SecretRole, UserKey @@ -59,7 +59,7 @@ class SecretRoleCSVForm(forms.ModelForm): # Secrets # -class SecretForm(BootstrapMixin, forms.ModelForm): +class SecretForm(BootstrapMixin, CustomFieldForm): plaintext = forms.CharField( max_length=65535, required=False, @@ -129,7 +129,7 @@ class SecretCSVForm(forms.ModelForm): return s -class SecretBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm): +class SecretBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm): pk = forms.ModelMultipleChoiceField(queryset=Secret.objects.all(), widget=forms.MultipleHiddenInput) role = forms.ModelChoiceField(queryset=SecretRole.objects.all(), required=False) name = forms.CharField(max_length=100, required=False) @@ -138,7 +138,8 @@ class SecretBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm): nullable_fields = ['name'] -class SecretFilterForm(BootstrapMixin, forms.Form): +class SecretFilterForm(BootstrapMixin, CustomFieldFilterForm): + model = Secret q = forms.CharField(required=False, label='Search') role = FilterChoiceField( queryset=SecretRole.objects.annotate(filter_count=Count('secrets')), diff --git a/netbox/secrets/models.py b/netbox/secrets/models.py index bd1c6e878..9cb2b3892 100644 --- a/netbox/secrets/models.py +++ b/netbox/secrets/models.py @@ -8,12 +8,14 @@ from Crypto.Util import strxor from django.conf import settings from django.contrib.auth.hashers import make_password, check_password from django.contrib.auth.models import Group, User +from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import ValidationError from django.db import models from django.urls import reverse from django.utils.encoding import force_bytes, python_2_unicode_compatible from taggit.managers import TaggableManager +from extras.models import CustomFieldModel from utilities.models import ChangeLoggedModel from .exceptions import InvalidKey from .hashers import SecretValidationHasher @@ -311,7 +313,7 @@ class SecretRole(ChangeLoggedModel): @python_2_unicode_compatible -class Secret(ChangeLoggedModel): +class Secret(ChangeLoggedModel, CustomFieldModel): """ A Secret stores an AES256-encrypted copy of sensitive data, such as passwords or secret keys. An irreversible SHA-256 hash is stored along with the ciphertext for validation upon decryption. Each Secret is assigned to a @@ -343,6 +345,11 @@ class Secret(ChangeLoggedModel): max_length=128, editable=False ) + custom_field_values = GenericRelation( + to='extras.CustomFieldValue', + content_type_field='obj_type', + object_id_field='obj_id' + ) tags = TaggableManager() diff --git a/netbox/templates/secrets/secret.html b/netbox/templates/secrets/secret.html index 05488bba6..5b670f2c8 100644 --- a/netbox/templates/secrets/secret.html +++ b/netbox/templates/secrets/secret.html @@ -69,7 +69,7 @@ - {% include 'extras/inc/tags_panel.html' with tags=secret.tags.all url='secrets:secret_list' %} + {% include 'inc/custom_fields_panel.html' with custom_fields=secret.get_custom_fields %}