views.py 7.3 KB

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