views.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. from __future__ import unicode_literals
  2. import base64
  3. from django.contrib import messages
  4. from django.contrib.auth.decorators import permission_required, login_required
  5. from django.contrib.auth.mixins import PermissionRequiredMixin
  6. from django.db import transaction, IntegrityError
  7. from django.db.models import Count
  8. from django.shortcuts import get_object_or_404, redirect, render
  9. from django.urls import reverse
  10. from django.utils.decorators import method_decorator
  11. from django.views.generic import View
  12. from dcim.models import Device
  13. from utilities.views import (
  14. BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
  15. )
  16. from . import filters, forms, tables
  17. from .decorators import userkey_required
  18. from .models import SecretRole, Secret, SessionKey
  19. def get_session_key(request):
  20. """
  21. Extract and decode the session key sent with a request. Returns None if no session key was provided.
  22. """
  23. session_key = request.COOKIES.get('session_key', None)
  24. if session_key is not None:
  25. return base64.b64decode(session_key)
  26. return session_key
  27. #
  28. # Secret roles
  29. #
  30. class SecretRoleListView(ObjectListView):
  31. queryset = SecretRole.objects.annotate(secret_count=Count('secrets'))
  32. table = tables.SecretRoleTable
  33. template_name = 'secrets/secretrole_list.html'
  34. class SecretRoleEditView(PermissionRequiredMixin, ObjectEditView):
  35. permission_required = 'secrets.change_secretrole'
  36. model = SecretRole
  37. form_class = forms.SecretRoleForm
  38. def get_return_url(self, request, obj):
  39. return reverse('secrets:secretrole_list')
  40. class SecretRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
  41. permission_required = 'secrets.delete_secretrole'
  42. cls = SecretRole
  43. default_return_url = 'secrets:secretrole_list'
  44. #
  45. # Secrets
  46. #
  47. @method_decorator(login_required, name='dispatch')
  48. class SecretListView(ObjectListView):
  49. queryset = Secret.objects.select_related('role', 'device')
  50. filter = filters.SecretFilter
  51. filter_form = forms.SecretFilterForm
  52. table = tables.SecretTable
  53. template_name = 'secrets/secret_list.html'
  54. @method_decorator(login_required, name='dispatch')
  55. class SecretView(View):
  56. def get(self, request, pk):
  57. secret = get_object_or_404(Secret, pk=pk)
  58. return render(request, 'secrets/secret.html', {
  59. 'secret': secret,
  60. })
  61. @permission_required('secrets.add_secret')
  62. @userkey_required()
  63. def secret_add(request, pk):
  64. # Retrieve device
  65. device = get_object_or_404(Device, pk=pk)
  66. secret = Secret(device=device)
  67. session_key = get_session_key(request)
  68. if request.method == 'POST':
  69. form = forms.SecretForm(request.POST, instance=secret)
  70. if form.is_valid():
  71. # We need a valid session key in order to create a Secret
  72. if session_key is None:
  73. form.add_error(None, "No session key was provided with the request. Unable to encrypt secret data.")
  74. # Create and encrypt the new Secret
  75. else:
  76. master_key = None
  77. try:
  78. sk = SessionKey.objects.get(userkey__user=request.user)
  79. master_key = sk.get_master_key(session_key)
  80. except SessionKey.DoesNotExist:
  81. form.add_error(None, "No session key found for this user.")
  82. if master_key is not None:
  83. secret = form.save(commit=False)
  84. secret.plaintext = str(form.cleaned_data['plaintext'])
  85. secret.encrypt(master_key)
  86. secret.save()
  87. messages.success(request, "Added new secret: {}.".format(secret))
  88. if '_addanother' in request.POST:
  89. return redirect('dcim:device_addsecret', pk=device.pk)
  90. else:
  91. return redirect('secrets:secret', pk=secret.pk)
  92. else:
  93. form = forms.SecretForm(instance=secret)
  94. return render(request, 'secrets/secret_edit.html', {
  95. 'secret': secret,
  96. 'form': form,
  97. 'return_url': device.get_absolute_url(),
  98. })
  99. @permission_required('secrets.change_secret')
  100. @userkey_required()
  101. def secret_edit(request, pk):
  102. secret = get_object_or_404(Secret, pk=pk)
  103. session_key = get_session_key(request)
  104. if request.method == 'POST':
  105. form = forms.SecretForm(request.POST, instance=secret)
  106. if form.is_valid():
  107. # Re-encrypt the Secret if a plaintext and session key have been provided.
  108. if form.cleaned_data['plaintext'] and session_key is not None:
  109. # Retrieve the master key using the provided session key
  110. master_key = None
  111. try:
  112. sk = SessionKey.objects.get(userkey__user=request.user)
  113. master_key = sk.get_master_key(session_key)
  114. except SessionKey.DoesNotExist:
  115. form.add_error(None, "No session key found for this user.")
  116. # Create and encrypt the new Secret
  117. if master_key is not None:
  118. secret = form.save(commit=False)
  119. secret.plaintext = str(form.cleaned_data['plaintext'])
  120. secret.encrypt(master_key)
  121. secret.save()
  122. messages.success(request, "Modified secret {}.".format(secret))
  123. return redirect('secrets:secret', pk=secret.pk)
  124. else:
  125. form.add_error(None, "Invalid session key. Unable to encrypt secret data.")
  126. # We can't save the plaintext without a session key.
  127. elif form.cleaned_data['plaintext']:
  128. form.add_error(None, "No session key was provided with the request. Unable to encrypt secret data.")
  129. # If no new plaintext was specified, a session key is not needed.
  130. else:
  131. secret = form.save()
  132. messages.success(request, "Modified secret {}.".format(secret))
  133. return redirect('secrets:secret', pk=secret.pk)
  134. else:
  135. form = forms.SecretForm(instance=secret)
  136. return render(request, 'secrets/secret_edit.html', {
  137. 'secret': secret,
  138. 'form': form,
  139. 'return_url': reverse('secrets:secret', kwargs={'pk': secret.pk}),
  140. })
  141. class SecretDeleteView(PermissionRequiredMixin, ObjectDeleteView):
  142. permission_required = 'secrets.delete_secret'
  143. model = Secret
  144. default_return_url = 'secrets:secret_list'
  145. class SecretBulkImportView(BulkImportView):
  146. permission_required = 'ipam.add_vlan'
  147. model_form = forms.SecretCSVForm
  148. table = tables.SecretTable
  149. default_return_url = 'secrets:secret_list'
  150. master_key = None
  151. def _save_obj(self, obj_form):
  152. """
  153. Encrypt each object before saving it to the database.
  154. """
  155. obj = obj_form.save(commit=False)
  156. obj.encrypt(self.master_key)
  157. obj.save()
  158. return obj
  159. def post(self, request):
  160. # Grab the session key from cookies.
  161. session_key = request.COOKIES.get('session_key')
  162. if session_key:
  163. # Attempt to derive the master key using the provided session key.
  164. try:
  165. sk = SessionKey.objects.get(userkey__user=request.user)
  166. self.master_key = sk.get_master_key(base64.b64decode(session_key))
  167. except SessionKey.DoesNotExist:
  168. messages.error(request, "No session key found for this user.")
  169. if self.master_key is not None:
  170. return super(SecretBulkImportView, self).post(request)
  171. else:
  172. messages.error(request, "Invalid private key! Unable to encrypt secret data.")
  173. else:
  174. messages.error(request, "No session key was provided with the request. Unable to encrypt secret data.")
  175. return render(request, self.template_name, {
  176. 'form': self._import_form(request.POST),
  177. 'fields': self.model_form().fields,
  178. 'obj_type': self.model_form._meta.model._meta.verbose_name,
  179. 'return_url': self.default_return_url,
  180. })
  181. class SecretBulkEditView(PermissionRequiredMixin, BulkEditView):
  182. permission_required = 'secrets.change_secret'
  183. cls = Secret
  184. filter = filters.SecretFilter
  185. form = forms.SecretBulkEditForm
  186. template_name = 'secrets/secret_bulk_edit.html'
  187. default_return_url = 'secrets:secret_list'
  188. class SecretBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
  189. permission_required = 'secrets.delete_secret'
  190. cls = Secret
  191. filter = filters.SecretFilter
  192. default_return_url = 'secrets:secret_list'