1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Update secrets API, views

This commit is contained in:
Jeremy Stretch
2020-09-18 16:58:51 -04:00
parent b2a14d4654
commit 0b33c53f47
7 changed files with 53 additions and 29 deletions

View File

@ -1,10 +1,12 @@
from django.contrib.contenttypes.models import ContentType
from drf_yasg.utils import swagger_serializer_method
from rest_framework import serializers from rest_framework import serializers
from dcim.api.nested_serializers import NestedDeviceSerializer
from extras.api.customfields import CustomFieldModelSerializer from extras.api.customfields import CustomFieldModelSerializer
from extras.api.serializers import TaggedObjectSerializer from extras.api.serializers import TaggedObjectSerializer
from secrets.constants import SECRET_ASSIGNMENT_MODELS
from secrets.models import Secret, SecretRole from secrets.models import Secret, SecretRole
from utilities.api import ValidatedModelSerializer from utilities.api import ContentTypeField, ValidatedModelSerializer, get_serializer_for_model
from .nested_serializers import * from .nested_serializers import *
@ -23,18 +25,27 @@ class SecretRoleSerializer(ValidatedModelSerializer):
class SecretSerializer(TaggedObjectSerializer, CustomFieldModelSerializer): class SecretSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='secrets-api:secret-detail') url = serializers.HyperlinkedIdentityField(view_name='secrets-api:secret-detail')
device = NestedDeviceSerializer() assigned_object_type = ContentTypeField(
queryset=ContentType.objects.filter(SECRET_ASSIGNMENT_MODELS)
)
assigned_object = serializers.SerializerMethodField(read_only=True)
role = NestedSecretRoleSerializer() role = NestedSecretRoleSerializer()
plaintext = serializers.CharField() plaintext = serializers.CharField()
class Meta: class Meta:
model = Secret model = Secret
fields = [ fields = [
'id', 'url', 'device', 'role', 'name', 'plaintext', 'hash', 'tags', 'custom_fields', 'created', 'id', 'url', 'assigned_object_type', 'assigned_object_id', 'assigned_object', 'role', 'name', 'plaintext',
'last_updated', 'hash', 'tags', 'custom_fields', 'created', 'last_updated',
] ]
validators = [] validators = []
@swagger_serializer_method(serializer_or_field=serializers.DictField)
def get_assigned_object(self, obj):
serializer = get_serializer_for_model(obj.assigned_object, prefix='Nested')
context = {'request': self.context['request']}
return serializer(obj.assigned_object, context=context).data
def validate(self, data): def validate(self, data):
# Encrypt plaintext data using the master key provided from the view context # Encrypt plaintext data using the master key provided from the view context

View File

@ -46,9 +46,7 @@ class SecretRoleViewSet(ModelViewSet):
# #
class SecretViewSet(ModelViewSet): class SecretViewSet(ModelViewSet):
queryset = Secret.objects.prefetch_related( queryset = Secret.objects.prefetch_related('role', 'tags')
'device__primary_ip4', 'device__primary_ip6', 'role', 'tags',
)
serializer_class = serializers.SecretSerializer serializer_class = serializers.SecretSerializer
filterset_class = filters.SecretFilterSet filterset_class = filters.SecretFilterSet

View File

@ -1,5 +1,13 @@
from django.db.models import Q
# #
# Secrets # Secrets
# #
SECRET_ASSIGNMENT_MODELS = Q(
Q(app_label='dcim', model='device') |
Q(app_label='virtualization', model='virtualmachine')
)
SECRET_PLAINTEXT_MAX_LENGTH = 65535 SECRET_PLAINTEXT_MAX_LENGTH = 65535

View File

@ -1,6 +1,7 @@
from Crypto.Cipher import PKCS1_OAEP from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA from Crypto.PublicKey import RSA
from django import forms from django import forms
from django.contrib.contenttypes.models import ContentType
from dcim.models import Device from dcim.models import Device
from extras.forms import ( from extras.forms import (
@ -142,10 +143,11 @@ class SecretForm(BootstrapMixin, CustomFieldModelForm):
class SecretCSVForm(CustomFieldModelCSVForm): class SecretCSVForm(CustomFieldModelCSVForm):
device = CSVModelChoiceField( assigned_object_type = CSVModelChoiceField(
queryset=Device.objects.all(), queryset=ContentType.objects.all(),
to_field_name='name', limit_choices_to=SECRET_ASSIGNMENT_MODELS,
help_text='Assigned device' to_field_name='model',
help_text='Side A type'
) )
role = CSVModelChoiceField( role = CSVModelChoiceField(
queryset=SecretRole.objects.all(), queryset=SecretRole.objects.all(),

View File

@ -80,9 +80,9 @@ class SecretTest(APIViewTestCases.APIViewTestCase):
SecretRole.objects.bulk_create(secret_roles) SecretRole.objects.bulk_create(secret_roles)
secrets = ( secrets = (
Secret(device=device, role=secret_roles[0], name='Secret 1', plaintext='ABC'), Secret(assigned_object=device, role=secret_roles[0], name='Secret 1', plaintext='ABC'),
Secret(device=device, role=secret_roles[0], name='Secret 2', plaintext='DEF'), Secret(assigned_object=device, role=secret_roles[0], name='Secret 2', plaintext='DEF'),
Secret(device=device, role=secret_roles[0], name='Secret 3', plaintext='GHI'), Secret(assigned_object=device, role=secret_roles[0], name='Secret 3', plaintext='GHI'),
) )
for secret in secrets: for secret in secrets:
secret.encrypt(self.master_key) secret.encrypt(self.master_key)
@ -90,19 +90,22 @@ class SecretTest(APIViewTestCases.APIViewTestCase):
self.create_data = [ self.create_data = [
{ {
'device': device.pk, 'assigned_object_type': 'dcim.device',
'assigned_object_id': device.pk,
'role': secret_roles[1].pk, 'role': secret_roles[1].pk,
'name': 'Secret 4', 'name': 'Secret 4',
'plaintext': 'JKL', 'plaintext': 'JKL',
}, },
{ {
'device': device.pk, 'assigned_object_type': 'dcim.device',
'assigned_object_id': device.pk,
'role': secret_roles[1].pk, 'role': secret_roles[1].pk,
'name': 'Secret 5', 'name': 'Secret 5',
'plaintext': 'MNO', 'plaintext': 'MNO',
}, },
{ {
'device': device.pk, 'assigned_object_type': 'dcim.device',
'assigned_object_id': device.pk,
'role': secret_roles[1].pk, 'role': secret_roles[1].pk,
'name': 'Secret 6', 'name': 'Secret 6',
'plaintext': 'PQR', 'plaintext': 'PQR',

View File

@ -69,13 +69,14 @@ class SecretTestCase(
# Create one secret per device to allow bulk-editing of names (which must be unique per device/role) # Create one secret per device to allow bulk-editing of names (which must be unique per device/role)
Secret.objects.bulk_create(( Secret.objects.bulk_create((
Secret(device=devices[0], role=secretroles[0], name='Secret 1', ciphertext=b'1234567890'), Secret(assigned_object=devices[0], role=secretroles[0], name='Secret 1', ciphertext=b'1234567890'),
Secret(device=devices[1], role=secretroles[0], name='Secret 2', ciphertext=b'1234567890'), Secret(assigned_object=devices[1], role=secretroles[0], name='Secret 2', ciphertext=b'1234567890'),
Secret(device=devices[2], role=secretroles[0], name='Secret 3', ciphertext=b'1234567890'), Secret(assigned_object=devices[2], role=secretroles[0], name='Secret 3', ciphertext=b'1234567890'),
)) ))
cls.form_data = { cls.form_data = {
'device': devices[1].pk, 'assigned_object_type': 'dcim.device',
'assigned_object_id': devices[1].pk,
'role': secretroles[1].pk, 'role': secretroles[1].pk,
'name': 'Secret X', 'name': 'Secret X',
} }
@ -100,11 +101,12 @@ class SecretTestCase(
def test_import_objects(self): def test_import_objects(self):
self.add_permissions('secrets.add_secret') self.add_permissions('secrets.add_secret')
device = Device.objects.get(name='Device 1')
csv_data = ( csv_data = (
"device,role,name,plaintext", "assigned_object_type,assigned_object_id,role,name,plaintext",
"Device 1,Secret Role 1,Secret 4,abcdefghij", f"device,{device.pk},Secret Role 1,Secret 4,abcdefghij",
"Device 1,Secret Role 1,Secret 5,abcdefghij", f"device,{device.pk},Secret Role 1,Secret 5,abcdefghij",
"Device 1,Secret Role 1,Secret 6,abcdefghij", f"device,{device.pk},Secret Role 1,Secret 6,abcdefghij",
) )
# Set the session_key cookie on the request # Set the session_key cookie on the request

View File

@ -58,7 +58,7 @@ class SecretRoleBulkDeleteView(BulkDeleteView):
# #
class SecretListView(ObjectListView): class SecretListView(ObjectListView):
queryset = Secret.objects.prefetch_related('role', 'device') queryset = Secret.objects.prefetch_related('role', 'tags')
filterset = filters.SecretFilterSet filterset = filters.SecretFilterSet
filterset_form = forms.SecretFilterForm filterset_form = forms.SecretFilterForm
table = tables.SecretTable table = tables.SecretTable
@ -198,13 +198,13 @@ class SecretBulkImportView(BulkImportView):
class SecretBulkEditView(BulkEditView): class SecretBulkEditView(BulkEditView):
queryset = Secret.objects.prefetch_related('role', 'device') queryset = Secret.objects.prefetch_related('role')
filterset = filters.SecretFilterSet filterset = filters.SecretFilterSet
table = tables.SecretTable table = tables.SecretTable
form = forms.SecretBulkEditForm form = forms.SecretBulkEditForm
class SecretBulkDeleteView(BulkDeleteView): class SecretBulkDeleteView(BulkDeleteView):
queryset = Secret.objects.prefetch_related('role', 'device') queryset = Secret.objects.prefetch_related('role')
filterset = filters.SecretFilterSet filterset = filters.SecretFilterSet
table = tables.SecretTable table = tables.SecretTable