mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
Implemented full read/write support for secrets
This commit is contained in:
parent
07a2b136b8
commit
e3ae013e42
@ -4,13 +4,11 @@ from Crypto.PublicKey import RSA
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponseBadRequest
|
||||
|
||||
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.renderers import JSONRenderer
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import GenericViewSet, ModelViewSet, ViewSet
|
||||
from rest_framework.viewsets import ModelViewSet, ViewSet
|
||||
|
||||
from extras.api.renderers import FormlessBrowsableAPIRenderer, FreeRADIUSClientsRenderer
|
||||
from secrets.exceptions import InvalidSessionKey
|
||||
from secrets.filters import SecretFilter
|
||||
from secrets.models import Secret, SecretRole, SessionKey, UserKey
|
||||
@ -38,7 +36,6 @@ class SecretRoleViewSet(ModelViewSet):
|
||||
# Secrets
|
||||
#
|
||||
|
||||
# TODO: Need to implement custom create() and update() methods to handle secret encryption.
|
||||
class SecretViewSet(WritableSerializerMixin, ModelViewSet):
|
||||
queryset = Secret.objects.select_related(
|
||||
'device__primary_ip4', 'device__primary_ip6', 'role',
|
||||
@ -48,11 +45,21 @@ class SecretViewSet(WritableSerializerMixin, ModelViewSet):
|
||||
serializer_class = serializers.SecretSerializer
|
||||
write_serializer_class = serializers.WritableSecretSerializer
|
||||
filter_class = SecretFilter
|
||||
# DRF's BrowsableAPIRenderer can't support passing the secret key as a header, so we disable it.
|
||||
renderer_classes = [FormlessBrowsableAPIRenderer, JSONRenderer, FreeRADIUSClientsRenderer]
|
||||
|
||||
master_key = None
|
||||
|
||||
def _get_encrypted_fields(self, serializer):
|
||||
"""
|
||||
Since we can't call encrypt() on the serializer like we can on the Secret model, we need to calculate the
|
||||
ciphertext and hash values by encrypting a dummy copy. These can be passed to the serializer's save() method.
|
||||
"""
|
||||
s = Secret(plaintext=serializer.validated_data['plaintext'])
|
||||
s.encrypt(self.master_key)
|
||||
return ({
|
||||
'ciphertext': s.ciphertext,
|
||||
'hash': s.hash,
|
||||
})
|
||||
|
||||
def initial(self, request, *args, **kwargs):
|
||||
|
||||
super(SecretViewSet, self).initial(request, *args, **kwargs)
|
||||
@ -66,13 +73,18 @@ class SecretViewSet(WritableSerializerMixin, ModelViewSet):
|
||||
else:
|
||||
session_key = None
|
||||
|
||||
# We can't encrypt secret plaintext without a session key.
|
||||
# assert False, self.action
|
||||
if self.action in ['create', 'update'] and session_key is None:
|
||||
raise ValidationError("A session key must be provided when creating or updating secrets.")
|
||||
|
||||
# Attempt to retrieve the master key for encryption/decryption if a session key has been provided.
|
||||
if session_key is not None:
|
||||
try:
|
||||
sk = SessionKey.objects.get(userkey__user=request.user)
|
||||
self.master_key = sk.get_master_key(session_key)
|
||||
except (SessionKey.DoesNotExist, InvalidSessionKey):
|
||||
return HttpResponseBadRequest("Invalid session key.")
|
||||
raise ValidationError("Invalid session key.")
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
|
||||
@ -107,6 +119,12 @@ class SecretViewSet(WritableSerializerMixin, ModelViewSet):
|
||||
serializer = self.get_serializer(queryset, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
serializer.save(**self._get_encrypted_fields(serializer))
|
||||
|
||||
def perform_update(self, serializer):
|
||||
serializer.save(**self._get_encrypted_fields(serializer))
|
||||
|
||||
|
||||
class GetSessionKeyViewSet(ViewSet):
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user