views.py 9.0 KB

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