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:
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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(),
|
||||||
|
@ -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',
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user