views.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. from django.conf import settings
  2. from django.contrib import messages
  3. from django.contrib.auth import login as auth_login, logout as auth_logout, update_session_auth_hash
  4. from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
  5. from django.contrib.auth.models import update_last_login
  6. from django.contrib.auth.signals import user_logged_in
  7. from django.http import HttpResponseForbidden, HttpResponseRedirect
  8. from django.shortcuts import get_object_or_404, redirect, render
  9. from django.urls import reverse
  10. from django.utils.decorators import method_decorator
  11. from django.utils.http import is_safe_url
  12. from django.views.decorators.debug import sensitive_post_parameters
  13. from django.views.generic import View
  14. from secrets.forms import UserKeyForm
  15. from secrets.models import SessionKey, UserKey
  16. from utilities.forms import ConfirmationForm
  17. from .forms import LoginForm, PasswordChangeForm, TokenForm
  18. from .models import Token
  19. #
  20. # Login/logout
  21. #
  22. class LoginView(View):
  23. template_name = 'login.html'
  24. @method_decorator(sensitive_post_parameters('password'))
  25. def dispatch(self, *args, **kwargs):
  26. return super().dispatch(*args, **kwargs)
  27. def get(self, request):
  28. form = LoginForm(request)
  29. return render(request, self.template_name, {
  30. 'form': form,
  31. })
  32. def post(self, request):
  33. form = LoginForm(request, data=request.POST)
  34. if form.is_valid():
  35. # Determine where to direct user after successful login
  36. redirect_to = request.POST.get('next', '')
  37. if not is_safe_url(url=redirect_to, allowed_hosts=request.get_host()):
  38. redirect_to = reverse('home')
  39. # If maintenance mode is enabled, assume the database is read-only, and disable updating the user's
  40. # last_login time upon authentication.
  41. if settings.MAINTENANCE_MODE:
  42. user_logged_in.disconnect(update_last_login, dispatch_uid='update_last_login')
  43. # Authenticate user
  44. auth_login(request, form.get_user())
  45. messages.info(request, "Logged in as {}.".format(request.user))
  46. return HttpResponseRedirect(redirect_to)
  47. return render(request, self.template_name, {
  48. 'form': form,
  49. })
  50. class LogoutView(View):
  51. def get(self, request):
  52. # Log out the user
  53. auth_logout(request)
  54. messages.info(request, "You have logged out.")
  55. # Delete session key cookie (if set) upon logout
  56. response = HttpResponseRedirect(reverse('home'))
  57. response.delete_cookie('session_key')
  58. return response
  59. #
  60. # User profiles
  61. #
  62. class ProfileView(LoginRequiredMixin, View):
  63. template_name = 'users/profile.html'
  64. def get(self, request):
  65. return render(request, self.template_name, {
  66. 'active_tab': 'profile',
  67. })
  68. class ChangePasswordView(LoginRequiredMixin, View):
  69. template_name = 'users/change_password.html'
  70. def get(self, request):
  71. form = PasswordChangeForm(user=request.user)
  72. return render(request, self.template_name, {
  73. 'form': form,
  74. 'active_tab': 'change_password',
  75. })
  76. def post(self, request):
  77. form = PasswordChangeForm(user=request.user, data=request.POST)
  78. if form.is_valid():
  79. form.save()
  80. update_session_auth_hash(request, form.user)
  81. messages.success(request, "Your password has been changed successfully.")
  82. return redirect('user:profile')
  83. return render(request, self.template_name, {
  84. 'form': form,
  85. 'active_tab': 'change_password',
  86. })
  87. class UserKeyView(LoginRequiredMixin, View):
  88. template_name = 'users/userkey.html'
  89. def get(self, request):
  90. try:
  91. userkey = UserKey.objects.get(user=request.user)
  92. except UserKey.DoesNotExist:
  93. userkey = None
  94. return render(request, self.template_name, {
  95. 'userkey': userkey,
  96. 'active_tab': 'userkey',
  97. })
  98. class UserKeyEditView(LoginRequiredMixin, View):
  99. template_name = 'users/userkey_edit.html'
  100. def dispatch(self, request, *args, **kwargs):
  101. try:
  102. self.userkey = UserKey.objects.get(user=request.user)
  103. except UserKey.DoesNotExist:
  104. self.userkey = UserKey(user=request.user)
  105. return super().dispatch(request, *args, **kwargs)
  106. def get(self, request):
  107. form = UserKeyForm(instance=self.userkey)
  108. return render(request, self.template_name, {
  109. 'userkey': self.userkey,
  110. 'form': form,
  111. 'active_tab': 'userkey',
  112. })
  113. def post(self, request):
  114. form = UserKeyForm(data=request.POST, instance=self.userkey)
  115. if form.is_valid():
  116. uk = form.save(commit=False)
  117. uk.user = request.user
  118. uk.save()
  119. messages.success(request, "Your user key has been saved.")
  120. return redirect('user:userkey')
  121. return render(request, self.template_name, {
  122. 'userkey': self.userkey,
  123. 'form': form,
  124. 'active_tab': 'userkey',
  125. })
  126. class SessionKeyDeleteView(LoginRequiredMixin, View):
  127. def get(self, request):
  128. sessionkey = get_object_or_404(SessionKey, userkey__user=request.user)
  129. form = ConfirmationForm()
  130. return render(request, 'users/sessionkey_delete.html', {
  131. 'obj_type': sessionkey._meta.verbose_name,
  132. 'form': form,
  133. 'return_url': reverse('user:userkey'),
  134. })
  135. def post(self, request):
  136. sessionkey = get_object_or_404(SessionKey, userkey__user=request.user)
  137. form = ConfirmationForm(request.POST)
  138. if form.is_valid():
  139. # Delete session key
  140. sessionkey.delete()
  141. messages.success(request, "Session key deleted")
  142. # Delete cookie
  143. response = redirect('user:userkey')
  144. response.delete_cookie('session_key')
  145. return response
  146. return render(request, 'users/sessionkey_delete.html', {
  147. 'obj_type': sessionkey._meta.verbose_name,
  148. 'form': form,
  149. 'return_url': reverse('user:userkey'),
  150. })
  151. #
  152. # API tokens
  153. #
  154. class TokenListView(LoginRequiredMixin, View):
  155. def get(self, request):
  156. tokens = Token.objects.filter(user=request.user)
  157. return render(request, 'users/api_tokens.html', {
  158. 'tokens': tokens,
  159. 'active_tab': 'api_tokens',
  160. })
  161. class TokenEditView(LoginRequiredMixin, View):
  162. def get(self, request, pk=None):
  163. if pk is not None:
  164. if not request.user.has_perm('users.change_token'):
  165. return HttpResponseForbidden()
  166. token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
  167. else:
  168. if not request.user.has_perm('users.add_token'):
  169. return HttpResponseForbidden()
  170. token = Token(user=request.user)
  171. form = TokenForm(instance=token)
  172. return render(request, 'utilities/obj_edit.html', {
  173. 'obj': token,
  174. 'obj_type': token._meta.verbose_name,
  175. 'form': form,
  176. 'return_url': reverse('user:token_list'),
  177. })
  178. def post(self, request, pk=None):
  179. if pk is not None:
  180. token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
  181. form = TokenForm(request.POST, instance=token)
  182. else:
  183. token = Token()
  184. form = TokenForm(request.POST)
  185. if form.is_valid():
  186. token = form.save(commit=False)
  187. token.user = request.user
  188. token.save()
  189. msg = "Modified token {}".format(token) if pk else "Created token {}".format(token)
  190. messages.success(request, msg)
  191. if '_addanother' in request.POST:
  192. return redirect(request.path)
  193. else:
  194. return redirect('user:token_list')
  195. return render(request, 'utilities/obj_edit.html', {
  196. 'obj': token,
  197. 'obj_type': token._meta.verbose_name,
  198. 'form': form,
  199. 'return_url': reverse('user:token_list'),
  200. })
  201. class TokenDeleteView(PermissionRequiredMixin, View):
  202. permission_required = 'users.delete_token'
  203. def get(self, request, pk):
  204. token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
  205. initial_data = {
  206. 'return_url': reverse('user:token_list'),
  207. }
  208. form = ConfirmationForm(initial=initial_data)
  209. return render(request, 'utilities/obj_delete.html', {
  210. 'obj': token,
  211. 'obj_type': token._meta.verbose_name,
  212. 'form': form,
  213. 'return_url': reverse('user:token_list'),
  214. })
  215. def post(self, request, pk):
  216. token = get_object_or_404(Token.objects.filter(user=request.user), pk=pk)
  217. form = ConfirmationForm(request.POST)
  218. if form.is_valid():
  219. token.delete()
  220. messages.success(request, "Token deleted")
  221. return redirect('user:token_list')
  222. return render(request, 'utilities/obj_delete.html', {
  223. 'obj': token,
  224. 'obj_type': token._meta.verbose_name,
  225. 'form': form,
  226. 'return_url': reverse('user:token_list'),
  227. })