2
0

views.py 9.1 KB

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