mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Initial push to public repo
This commit is contained in:
0
netbox/secrets/api/__init__.py
Normal file
0
netbox/secrets/api/__init__.py
Normal file
39
netbox/secrets/api/serializers.py
Normal file
39
netbox/secrets/api/serializers.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from secrets.models import Secret, SecretRole
|
||||
|
||||
|
||||
#
|
||||
# SecretRoles
|
||||
#
|
||||
|
||||
class SecretRoleSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = SecretRole
|
||||
fields = ['id', 'name', 'slug']
|
||||
|
||||
|
||||
class SecretRoleNestedSerializer(SecretRoleSerializer):
|
||||
|
||||
class Meta(SecretRoleSerializer.Meta):
|
||||
pass
|
||||
|
||||
|
||||
#
|
||||
# Secrets
|
||||
#
|
||||
|
||||
# TODO: Serialize parent info
|
||||
class SecretSerializer(serializers.ModelSerializer):
|
||||
role = SecretRoleNestedSerializer()
|
||||
|
||||
class Meta:
|
||||
model = Secret
|
||||
fields = ['id', 'role', 'name', 'hash', 'created', 'last_modified']
|
||||
|
||||
|
||||
class SecretNestedSerializer(SecretSerializer):
|
||||
|
||||
class Meta(SecretSerializer.Meta):
|
||||
fields = ['id', 'name']
|
20
netbox/secrets/api/urls.py
Normal file
20
netbox/secrets/api/urls.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views import *
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
# Secrets
|
||||
url(r'^secrets/$', SecretListView.as_view(), name='secret_list'),
|
||||
url(r'^secrets/(?P<pk>\d+)/$', SecretDetailView.as_view(), name='secret_detail'),
|
||||
url(r'^secrets/(?P<pk>\d+)/decrypt/$', SecretDecryptView.as_view(), name='secret_decrypt'),
|
||||
|
||||
# Secret roles
|
||||
url(r'^secret-roles/$', SecretRoleListView.as_view(), name='secretrole_list'),
|
||||
url(r'^secret-roles/(?P<pk>\d+)/$', SecretRoleDetailView.as_view(), name='secretrole_detail'),
|
||||
|
||||
# Miscellaneous
|
||||
url(r'^generate-keys/$', RSAKeyGeneratorView.as_view(), name='generate_keys'),
|
||||
|
||||
]
|
104
netbox/secrets/api/views.py
Normal file
104
netbox/secrets/api/views.py
Normal file
@@ -0,0 +1,104 @@
|
||||
from Crypto.PublicKey import RSA
|
||||
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from rest_framework import generics
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from secrets.models import Secret, SecretRole, UserKey
|
||||
from .serializers import SecretRoleSerializer, SecretSerializer
|
||||
|
||||
|
||||
class SecretRoleListView(generics.ListAPIView):
|
||||
"""
|
||||
List all secret roles
|
||||
"""
|
||||
queryset = SecretRole.objects.all()
|
||||
serializer_class = SecretRoleSerializer
|
||||
|
||||
|
||||
class SecretRoleDetailView(generics.RetrieveAPIView):
|
||||
"""
|
||||
Retrieve a single secret role
|
||||
"""
|
||||
queryset = SecretRole.objects.all()
|
||||
serializer_class = SecretRoleSerializer
|
||||
|
||||
|
||||
class SecretListView(generics.ListAPIView):
|
||||
"""
|
||||
List secrets (filterable)
|
||||
"""
|
||||
queryset = Secret.objects.select_related('role')
|
||||
serializer_class = SecretSerializer
|
||||
#filter_class = SecretFilter
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
|
||||
class SecretDetailView(generics.RetrieveAPIView):
|
||||
"""
|
||||
Retrieve a single Secret
|
||||
"""
|
||||
queryset = Secret.objects.select_related('role')
|
||||
serializer_class = SecretSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
|
||||
class SecretDecryptView(APIView):
|
||||
"""
|
||||
Retrieve the plaintext from a stored Secret. The request must include a valid private key.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request, pk):
|
||||
|
||||
secret = get_object_or_404(Secret, pk=pk)
|
||||
private_key = request.POST.get('private_key')
|
||||
if not private_key:
|
||||
raise ValidationError("Private key is missing from request.")
|
||||
|
||||
# Retrieve the Secret's plaintext with the user's private key
|
||||
try:
|
||||
uk = UserKey.objects.get(user=request.user)
|
||||
except UserKey.DoesNotExist:
|
||||
return HttpResponseForbidden(reason="No UserKey found.")
|
||||
if not uk.is_active():
|
||||
return HttpResponseForbidden(reason="UserKey is inactive.")
|
||||
|
||||
# Attempt to decrypt the Secret.
|
||||
master_key = uk.get_master_key(private_key)
|
||||
if master_key is None:
|
||||
return HttpResponseForbidden(reason="Invalid secret key.")
|
||||
secret.decrypt(master_key)
|
||||
|
||||
return Response({
|
||||
'plaintext': secret.plaintext,
|
||||
})
|
||||
|
||||
|
||||
class RSAKeyGeneratorView(APIView):
|
||||
"""
|
||||
Generate a new RSA key pair for a user. Authenticated because it's a ripe avenue for DoS.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request):
|
||||
|
||||
# Determine what size key to generate
|
||||
key_size = request.GET.get('key_size', 2048)
|
||||
if key_size not in range(2048, 4097, 256):
|
||||
key_size = 2048
|
||||
|
||||
# Export RSA private and public keys in PEM format
|
||||
key = RSA.generate(key_size)
|
||||
private_key = key.exportKey('PEM')
|
||||
public_key = key.publickey().exportKey('PEM')
|
||||
|
||||
return Response({
|
||||
'private_key': private_key,
|
||||
'public_key': public_key,
|
||||
})
|
Reference in New Issue
Block a user