views.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. from django.apps import apps
  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.core.urlresolvers import reverse
  6. from django.db import transaction, IntegrityError
  7. from django.db.models import ProtectedError
  8. from django.shortcuts import get_object_or_404, redirect, render
  9. from django.utils.decorators import method_decorator
  10. from utilities.error_handlers import handle_protectederror
  11. from utilities.forms import ConfirmationForm
  12. from utilities.views import BulkEditView, BulkDeleteView, ObjectListView
  13. from .decorators import userkey_required
  14. from .filters import SecretFilter
  15. from .forms import SecretForm, SecretImportForm, SecretBulkEditForm, SecretBulkDeleteForm, SecretFilterForm
  16. from .models import Secret, UserKey
  17. from .tables import SecretTable, SecretBulkEditTable
  18. #
  19. # Secrets
  20. #
  21. @method_decorator(login_required, name='dispatch')
  22. class SecretListView(ObjectListView):
  23. queryset = Secret.objects.select_related('role').prefetch_related('parent')
  24. filter = SecretFilter
  25. filter_form = SecretFilterForm
  26. table = SecretTable
  27. edit_table = SecretBulkEditTable
  28. edit_table_permissions = ['secrets.change_secret', 'secrets.delete_secret']
  29. template_name = 'secrets/secret_list.html'
  30. @login_required
  31. def secret(request, pk):
  32. secret = get_object_or_404(Secret, pk=pk)
  33. return render(request, 'secrets/secret.html', {
  34. 'secret': secret,
  35. })
  36. @permission_required('secrets.add_secret')
  37. @userkey_required()
  38. def secret_add(request, parent_model, parent_pk):
  39. # Retrieve parent object
  40. parent_cls = apps.get_model(parent_model)
  41. parent = get_object_or_404(parent_cls, pk=parent_pk)
  42. secret = Secret(parent=parent)
  43. uk = UserKey.objects.get(user=request.user)
  44. if request.method == 'POST':
  45. form = SecretForm(request.POST, instance=secret)
  46. if form.is_valid():
  47. # Retrieve the master key from the current user's UserKey
  48. master_key = uk.get_master_key(form.cleaned_data['private_key'])
  49. if master_key is None:
  50. form.add_error(None, "Invalid private key! Unable to encrypt secret data.")
  51. # Create and encrypt the new Secret
  52. else:
  53. secret = form.save(commit=False)
  54. secret.plaintext = str(form.cleaned_data['plaintext'])
  55. secret.encrypt(master_key)
  56. secret.save()
  57. messages.success(request, "Added new secret: {0}".format(secret))
  58. if '_addanother' in request.POST:
  59. return redirect('secrets:secret_add')
  60. else:
  61. return redirect('secrets:secret', pk=secret.pk)
  62. else:
  63. form = SecretForm(instance=secret)
  64. return render(request, 'secrets/secret_edit.html', {
  65. 'secret': secret,
  66. 'form': form,
  67. 'cancel_url': parent.get_absolute_url(),
  68. })
  69. @permission_required('secrets.change_secret')
  70. @userkey_required()
  71. def secret_edit(request, pk):
  72. secret = get_object_or_404(Secret, pk=pk)
  73. uk = UserKey.objects.get(user=request.user)
  74. if request.method == 'POST':
  75. form = SecretForm(request.POST, instance=secret)
  76. if form.is_valid():
  77. # Re-encrypt the Secret if a plaintext has been specified.
  78. if form.cleaned_data['plaintext']:
  79. # Retrieve the master key from the current user's UserKey
  80. master_key = uk.get_master_key(form.cleaned_data['private_key'])
  81. if master_key is None:
  82. form.add_error(None, "Invalid private key! Unable to encrypt secret data.")
  83. # Create and encrypt the new Secret
  84. else:
  85. secret = form.save(commit=False)
  86. secret.plaintext = str(form.cleaned_data['plaintext'])
  87. secret.encrypt(master_key)
  88. secret.save()
  89. else:
  90. secret = form.save()
  91. messages.success(request, "Modified secret {0}".format(secret))
  92. return redirect('secrets:secret', pk=secret.pk)
  93. else:
  94. form = SecretForm(instance=secret)
  95. return render(request, 'secrets/secret_edit.html', {
  96. 'secret': secret,
  97. 'form': form,
  98. 'cancel_url': reverse('secrets:secret', kwargs={'pk': secret.pk}),
  99. })
  100. @permission_required('secrets.delete_secret')
  101. def secret_delete(request, pk):
  102. secret = get_object_or_404(Secret, pk=pk)
  103. if request.method == 'POST':
  104. form = ConfirmationForm(request.POST)
  105. if form.is_valid():
  106. try:
  107. secret.delete()
  108. messages.success(request, "Secret {0} has been deleted".format(secret))
  109. return redirect('secrets:secret_list')
  110. except ProtectedError, e:
  111. handle_protectederror(secret, request, e)
  112. return redirect('secrets:secret', pk=secret.pk)
  113. else:
  114. form = ConfirmationForm()
  115. return render(request, 'secrets/secret_delete.html', {
  116. 'secret': secret,
  117. 'form': form,
  118. 'cancel_url': reverse('secrets:secret', kwargs={'pk': secret.pk})
  119. })
  120. @permission_required('secrets.add_secret')
  121. @userkey_required()
  122. def secret_import(request):
  123. uk = UserKey.objects.get(user=request.user)
  124. if request.method == 'POST':
  125. form = SecretImportForm(request.POST)
  126. if form.is_valid():
  127. new_secrets = []
  128. # Retrieve the master key from the current user's UserKey
  129. master_key = uk.get_master_key(form.cleaned_data['private_key'])
  130. if master_key is None:
  131. form.add_error(None, "Invalid private key! Unable to encrypt secret data.")
  132. else:
  133. try:
  134. with transaction.atomic():
  135. for secret in form.cleaned_data['csv']:
  136. secret.encrypt(master_key)
  137. secret.save()
  138. new_secrets.append(secret)
  139. secret_table = SecretTable(new_secrets)
  140. messages.success(request, "Imported {} new secrets".format(len(new_secrets)))
  141. return render(request, 'import_success.html', {
  142. 'secret_table': secret_table,
  143. })
  144. except IntegrityError as e:
  145. form.add_error('csv', "Record {}: {}".format(len(new_secrets) + 1, e.__cause__))
  146. else:
  147. form = SecretImportForm()
  148. return render(request, 'secrets/secret_import.html', {
  149. 'form': form,
  150. 'cancel_url': reverse('secrets:secret_list'),
  151. })
  152. class SecretBulkEditView(PermissionRequiredMixin, BulkEditView):
  153. permission_required = 'secrets.change_secret'
  154. cls = Secret
  155. form = SecretBulkEditForm
  156. template_name = 'secrets/secret_bulk_edit.html'
  157. redirect_url = 'secrets:secret_list'
  158. def update_objects(self, pk_list, form):
  159. fields_to_update = {}
  160. for field in ['role', 'name']:
  161. if form.cleaned_data[field]:
  162. fields_to_update[field] = form.cleaned_data[field]
  163. updated_count = self.cls.objects.filter(pk__in=pk_list).update(**fields_to_update)
  164. messages.success(self.request, "Updated {} secrets".format(updated_count))
  165. class SecretBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
  166. permission_required = 'secrets.delete_secret'
  167. cls = Secret
  168. form = SecretBulkDeleteForm
  169. template_name = 'secrets/secret_bulk_delete.html'
  170. redirect_url = 'secrets:secret_list'