views.py 9.1 KB

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