123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- from __future__ import unicode_literals
- import base64
- from django.contrib import messages
- from django.contrib.auth.decorators import permission_required, login_required
- from django.contrib.auth.mixins import PermissionRequiredMixin
- from django.db.models import Count
- from django.shortcuts import get_object_or_404, redirect, render
- from django.urls import reverse
- from django.utils.decorators import method_decorator
- from django.views.generic import View
- from dcim.models import Device
- from utilities.views import (
- BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
- )
- from . import filters, forms, tables
- from .decorators import userkey_required
- from .models import SecretRole, Secret, SessionKey
- def get_session_key(request):
- """
- Extract and decode the session key sent with a request. Returns None if no session key was provided.
- """
- session_key = request.COOKIES.get('session_key', None)
- if session_key is not None:
- return base64.b64decode(session_key)
- return session_key
- #
- # Secret roles
- #
- class SecretRoleListView(ObjectListView):
- queryset = SecretRole.objects.annotate(secret_count=Count('secrets'))
- table = tables.SecretRoleTable
- template_name = 'secrets/secretrole_list.html'
- class SecretRoleCreateView(PermissionRequiredMixin, ObjectEditView):
- permission_required = 'secrets.add_secretrole'
- model = SecretRole
- model_form = forms.SecretRoleForm
- def get_return_url(self, request, obj):
- return reverse('secrets:secretrole_list')
- class SecretRoleEditView(SecretRoleCreateView):
- permission_required = 'secrets.change_secretrole'
- class SecretRoleBulkImportView(PermissionRequiredMixin, BulkImportView):
- permission_required = 'secrets.add_secretrole'
- model_form = forms.SecretRoleCSVForm
- table = tables.SecretRoleTable
- default_return_url = 'secrets:secretrole_list'
- class SecretRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
- permission_required = 'secrets.delete_secretrole'
- cls = SecretRole
- queryset = SecretRole.objects.annotate(secret_count=Count('secrets'))
- table = tables.SecretRoleTable
- default_return_url = 'secrets:secretrole_list'
- #
- # Secrets
- #
- @method_decorator(login_required, name='dispatch')
- class SecretListView(ObjectListView):
- queryset = Secret.objects.select_related('role', 'device')
- filter = filters.SecretFilter
- filter_form = forms.SecretFilterForm
- table = tables.SecretTable
- template_name = 'secrets/secret_list.html'
- @method_decorator(login_required, name='dispatch')
- class SecretView(View):
- def get(self, request, pk):
- secret = get_object_or_404(Secret, pk=pk)
- return render(request, 'secrets/secret.html', {
- 'secret': secret,
- })
- @permission_required('secrets.add_secret')
- @userkey_required()
- def secret_add(request, pk):
- # Retrieve device
- device = get_object_or_404(Device, pk=pk)
- secret = Secret(device=device)
- session_key = get_session_key(request)
- if request.method == 'POST':
- form = forms.SecretForm(request.POST, instance=secret)
- if form.is_valid():
- # We need a valid session key in order to create a Secret
- if session_key is None:
- form.add_error(None, "No session key was provided with the request. Unable to encrypt secret data.")
- # Create and encrypt the new Secret
- else:
- master_key = None
- try:
- sk = SessionKey.objects.get(userkey__user=request.user)
- master_key = sk.get_master_key(session_key)
- except SessionKey.DoesNotExist:
- form.add_error(None, "No session key found for this user.")
- if master_key is not None:
- secret = form.save(commit=False)
- secret.plaintext = str(form.cleaned_data['plaintext'])
- secret.encrypt(master_key)
- secret.save()
- messages.success(request, "Added new secret: {}.".format(secret))
- if '_addanother' in request.POST:
- return redirect('dcim:device_addsecret', pk=device.pk)
- else:
- return redirect('secrets:secret', pk=secret.pk)
- else:
- form = forms.SecretForm(instance=secret)
- return render(request, 'secrets/secret_edit.html', {
- 'secret': secret,
- 'form': form,
- 'return_url': device.get_absolute_url(),
- })
- @permission_required('secrets.change_secret')
- @userkey_required()
- def secret_edit(request, pk):
- secret = get_object_or_404(Secret, pk=pk)
- session_key = get_session_key(request)
- if request.method == 'POST':
- form = forms.SecretForm(request.POST, instance=secret)
- if form.is_valid():
- # Re-encrypt the Secret if a plaintext and session key have been provided.
- if form.cleaned_data['plaintext'] and session_key is not None:
- # Retrieve the master key using the provided session key
- master_key = None
- try:
- sk = SessionKey.objects.get(userkey__user=request.user)
- master_key = sk.get_master_key(session_key)
- except SessionKey.DoesNotExist:
- form.add_error(None, "No session key found for this user.")
- # Create and encrypt the new Secret
- if master_key is not None:
- secret = form.save(commit=False)
- secret.plaintext = form.cleaned_data['plaintext']
- secret.encrypt(master_key)
- secret.save()
- messages.success(request, "Modified secret {}.".format(secret))
- return redirect('secrets:secret', pk=secret.pk)
- else:
- form.add_error(None, "Invalid session key. Unable to encrypt secret data.")
- # We can't save the plaintext without a session key.
- elif form.cleaned_data['plaintext']:
- form.add_error(None, "No session key was provided with the request. Unable to encrypt secret data.")
- # If no new plaintext was specified, a session key is not needed.
- else:
- secret = form.save()
- messages.success(request, "Modified secret {}.".format(secret))
- return redirect('secrets:secret', pk=secret.pk)
- else:
- form = forms.SecretForm(instance=secret)
- return render(request, 'secrets/secret_edit.html', {
- 'secret': secret,
- 'form': form,
- 'return_url': reverse('secrets:secret', kwargs={'pk': secret.pk}),
- })
- class SecretDeleteView(PermissionRequiredMixin, ObjectDeleteView):
- permission_required = 'secrets.delete_secret'
- model = Secret
- default_return_url = 'secrets:secret_list'
- class SecretBulkImportView(BulkImportView):
- permission_required = 'ipam.add_vlan'
- model_form = forms.SecretCSVForm
- table = tables.SecretTable
- template_name = 'secrets/secret_import.html'
- default_return_url = 'secrets:secret_list'
- widget_attrs = {'class': 'requires-session-key'}
- master_key = None
- def _save_obj(self, obj_form):
- """
- Encrypt each object before saving it to the database.
- """
- obj = obj_form.save(commit=False)
- obj.encrypt(self.master_key)
- obj.save()
- return obj
- def post(self, request):
- # Grab the session key from cookies.
- session_key = request.COOKIES.get('session_key')
- if session_key:
- # Attempt to derive the master key using the provided session key.
- try:
- sk = SessionKey.objects.get(userkey__user=request.user)
- self.master_key = sk.get_master_key(base64.b64decode(session_key))
- except SessionKey.DoesNotExist:
- messages.error(request, "No session key found for this user.")
- if self.master_key is not None:
- return super(SecretBulkImportView, self).post(request)
- else:
- messages.error(request, "Invalid private key! Unable to encrypt secret data.")
- else:
- messages.error(request, "No session key was provided with the request. Unable to encrypt secret data.")
- return render(request, self.template_name, {
- 'form': self._import_form(request.POST),
- 'fields': self.model_form().fields,
- 'obj_type': self.model_form._meta.model._meta.verbose_name,
- 'return_url': self.default_return_url,
- })
- class SecretBulkEditView(PermissionRequiredMixin, BulkEditView):
- permission_required = 'secrets.change_secret'
- cls = Secret
- queryset = Secret.objects.select_related('role', 'device')
- filter = filters.SecretFilter
- table = tables.SecretTable
- form = forms.SecretBulkEditForm
- default_return_url = 'secrets:secret_list'
- class SecretBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
- permission_required = 'secrets.delete_secret'
- cls = Secret
- queryset = Secret.objects.select_related('role', 'device')
- filter = filters.SecretFilter
- table = tables.SecretTable
- default_return_url = 'secrets:secret_list'
|