views.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import base64
  2. import logging
  3. from django.contrib import messages
  4. from django.db.models import Count
  5. from django.shortcuts import get_object_or_404, redirect, render
  6. from django.utils.html import escape
  7. from django.utils.safestring import mark_safe
  8. from utilities.views import (
  9. BulkDeleteView, BulkEditView, BulkImportView, ObjectView, ObjectDeleteView, ObjectEditView, ObjectListView,
  10. )
  11. from . import filters, forms, tables
  12. from .models import SecretRole, Secret, SessionKey, UserKey
  13. def get_session_key(request):
  14. """
  15. Extract and decode the session key sent with a request. Returns None if no session key was provided.
  16. """
  17. session_key = request.COOKIES.get('session_key', None)
  18. if session_key is not None:
  19. return base64.b64decode(session_key)
  20. return session_key
  21. #
  22. # Secret roles
  23. #
  24. class SecretRoleListView(ObjectListView):
  25. queryset = SecretRole.objects.annotate(secret_count=Count('secrets')).order_by(*SecretRole._meta.ordering)
  26. table = tables.SecretRoleTable
  27. class SecretRoleEditView(ObjectEditView):
  28. queryset = SecretRole.objects.all()
  29. model_form = forms.SecretRoleForm
  30. class SecretRoleDeleteView(ObjectDeleteView):
  31. queryset = SecretRole.objects.all()
  32. class SecretRoleBulkImportView(BulkImportView):
  33. queryset = SecretRole.objects.all()
  34. model_form = forms.SecretRoleCSVForm
  35. table = tables.SecretRoleTable
  36. class SecretRoleBulkDeleteView(BulkDeleteView):
  37. queryset = SecretRole.objects.annotate(secret_count=Count('secrets')).order_by(*SecretRole._meta.ordering)
  38. table = tables.SecretRoleTable
  39. #
  40. # Secrets
  41. #
  42. class SecretListView(ObjectListView):
  43. queryset = Secret.objects.prefetch_related('role', 'device')
  44. filterset = filters.SecretFilterSet
  45. filterset_form = forms.SecretFilterForm
  46. table = tables.SecretTable
  47. action_buttons = ('import', 'export')
  48. class SecretView(ObjectView):
  49. queryset = Secret.objects.all()
  50. def get(self, request, pk):
  51. secret = get_object_or_404(self.queryset, pk=pk)
  52. return render(request, 'secrets/secret.html', {
  53. 'secret': secret,
  54. })
  55. class SecretEditView(ObjectEditView):
  56. queryset = Secret.objects.all()
  57. model_form = forms.SecretForm
  58. template_name = 'secrets/secret_edit.html'
  59. def dispatch(self, request, *args, **kwargs):
  60. # Check that the user has a valid UserKey
  61. try:
  62. uk = UserKey.objects.get(user=request.user)
  63. except UserKey.DoesNotExist:
  64. messages.warning(request, "This operation requires an active user key, but you don't have one.")
  65. return redirect('user:userkey')
  66. if not uk.is_active():
  67. messages.warning(request, "This operation is not available. Your user key has not been activated.")
  68. return redirect('user:userkey')
  69. return super().dispatch(request, *args, **kwargs)
  70. def post(self, request, *args, **kwargs):
  71. logger = logging.getLogger('netbox.views.ObjectEditView')
  72. session_key = get_session_key(request)
  73. secret = self.get_object(kwargs)
  74. form = self.model_form(request.POST, instance=secret)
  75. if form.is_valid():
  76. logger.debug("Form validation was successful")
  77. # We must have a session key in order to create a secret or update the plaintext of an existing secret
  78. if (form.cleaned_data['plaintext'] or secret.pk is None) and session_key is None:
  79. logger.debug("Unable to proceed: No session key was provided with the request")
  80. form.add_error(None, "No session key was provided with the request. Unable to encrypt secret data.")
  81. else:
  82. master_key = None
  83. try:
  84. sk = SessionKey.objects.get(userkey__user=request.user)
  85. master_key = sk.get_master_key(session_key)
  86. except SessionKey.DoesNotExist:
  87. logger.debug("Unable to proceed: User has no session key assigned")
  88. form.add_error(None, "No session key found for this user.")
  89. if master_key is not None:
  90. logger.debug("Successfully resolved master key for encryption")
  91. secret = form.save(commit=False)
  92. if form.cleaned_data['plaintext']:
  93. secret.plaintext = str(form.cleaned_data['plaintext'])
  94. secret.encrypt(master_key)
  95. secret.save()
  96. form.save_m2m()
  97. msg = '{} secret'.format('Created' if not form.instance.pk else 'Modified')
  98. logger.info(f"{msg} {secret} (PK: {secret.pk})")
  99. msg = '{} <a href="{}">{}</a>'.format(msg, secret.get_absolute_url(), escape(secret))
  100. messages.success(request, mark_safe(msg))
  101. return redirect(self.get_return_url(request, secret))
  102. else:
  103. logger.debug("Form validation failed")
  104. return render(request, self.template_name, {
  105. 'obj': secret,
  106. 'obj_type': self.queryset.model._meta.verbose_name,
  107. 'form': form,
  108. 'return_url': self.get_return_url(request, secret),
  109. })
  110. class SecretDeleteView(ObjectDeleteView):
  111. queryset = Secret.objects.all()
  112. class SecretBulkImportView(BulkImportView):
  113. queryset = Secret.objects.all()
  114. model_form = forms.SecretCSVForm
  115. table = tables.SecretTable
  116. template_name = 'secrets/secret_import.html'
  117. widget_attrs = {'class': 'requires-session-key'}
  118. master_key = None
  119. def _save_obj(self, obj_form, request):
  120. """
  121. Encrypt each object before saving it to the database.
  122. """
  123. obj = obj_form.save(commit=False)
  124. obj.encrypt(self.master_key)
  125. obj.save()
  126. return obj
  127. def post(self, request):
  128. # Grab the session key from cookies.
  129. session_key = request.COOKIES.get('session_key')
  130. if session_key:
  131. # Attempt to derive the master key using the provided session key.
  132. try:
  133. sk = SessionKey.objects.get(userkey__user=request.user)
  134. self.master_key = sk.get_master_key(base64.b64decode(session_key))
  135. except SessionKey.DoesNotExist:
  136. messages.error(request, "No session key found for this user.")
  137. if self.master_key is not None:
  138. return super().post(request)
  139. else:
  140. messages.error(request, "Invalid private key! Unable to encrypt secret data.")
  141. else:
  142. messages.error(request, "No session key was provided with the request. Unable to encrypt secret data.")
  143. return render(request, self.template_name, {
  144. 'form': self._import_form(request.POST),
  145. 'fields': self.model_form().fields,
  146. 'obj_type': self.model_form._meta.model._meta.verbose_name,
  147. 'return_url': self.get_return_url(request),
  148. })
  149. class SecretBulkEditView(BulkEditView):
  150. queryset = Secret.objects.prefetch_related('role', 'device')
  151. filterset = filters.SecretFilterSet
  152. table = tables.SecretTable
  153. form = forms.SecretBulkEditForm
  154. class SecretBulkDeleteView(BulkDeleteView):
  155. queryset = Secret.objects.prefetch_related('role', 'device')
  156. filterset = filters.SecretFilterSet
  157. table = tables.SecretTable