mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-25 18:08:38 -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.core.urlresolvers import reverse
|
||||||
from django.http import HttpResponseBadRequest
|
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.permissions import IsAuthenticated
|
||||||
from rest_framework.renderers import JSONRenderer
|
|
||||||
from rest_framework.response import Response
|
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.exceptions import InvalidSessionKey
|
||||||
from secrets.filters import SecretFilter
|
from secrets.filters import SecretFilter
|
||||||
from secrets.models import Secret, SecretRole, SessionKey, UserKey
|
from secrets.models import Secret, SecretRole, SessionKey, UserKey
|
||||||
@ -38,7 +36,6 @@ class SecretRoleViewSet(ModelViewSet):
|
|||||||
# Secrets
|
# Secrets
|
||||||
#
|
#
|
||||||
|
|
||||||
# TODO: Need to implement custom create() and update() methods to handle secret encryption.
|
|
||||||
class SecretViewSet(WritableSerializerMixin, ModelViewSet):
|
class SecretViewSet(WritableSerializerMixin, ModelViewSet):
|
||||||
queryset = Secret.objects.select_related(
|
queryset = Secret.objects.select_related(
|
||||||
'device__primary_ip4', 'device__primary_ip6', 'role',
|
'device__primary_ip4', 'device__primary_ip6', 'role',
|
||||||
@ -48,11 +45,21 @@ class SecretViewSet(WritableSerializerMixin, ModelViewSet):
|
|||||||
serializer_class = serializers.SecretSerializer
|
serializer_class = serializers.SecretSerializer
|
||||||
write_serializer_class = serializers.WritableSecretSerializer
|
write_serializer_class = serializers.WritableSecretSerializer
|
||||||
filter_class = SecretFilter
|
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
|
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):
|
def initial(self, request, *args, **kwargs):
|
||||||
|
|
||||||
super(SecretViewSet, self).initial(request, *args, **kwargs)
|
super(SecretViewSet, self).initial(request, *args, **kwargs)
|
||||||
@ -66,13 +73,18 @@ class SecretViewSet(WritableSerializerMixin, ModelViewSet):
|
|||||||
else:
|
else:
|
||||||
session_key = None
|
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.
|
# Attempt to retrieve the master key for encryption/decryption if a session key has been provided.
|
||||||
if session_key is not None:
|
if session_key is not None:
|
||||||
try:
|
try:
|
||||||
sk = SessionKey.objects.get(userkey__user=request.user)
|
sk = SessionKey.objects.get(userkey__user=request.user)
|
||||||
self.master_key = sk.get_master_key(session_key)
|
self.master_key = sk.get_master_key(session_key)
|
||||||
except (SessionKey.DoesNotExist, InvalidSessionKey):
|
except (SessionKey.DoesNotExist, InvalidSessionKey):
|
||||||
return HttpResponseBadRequest("Invalid session key.")
|
raise ValidationError("Invalid session key.")
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
|
|
||||||
@ -107,6 +119,12 @@ class SecretViewSet(WritableSerializerMixin, ModelViewSet):
|
|||||||
serializer = self.get_serializer(queryset, many=True)
|
serializer = self.get_serializer(queryset, many=True)
|
||||||
return Response(serializer.data)
|
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):
|
class GetSessionKeyViewSet(ViewSet):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user