views.py 8.9 KB

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