mixins.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. from urllib.parse import urlparse
  2. from django.conf import settings
  3. from django.contrib.auth import REDIRECT_FIELD_NAME
  4. from django.contrib.auth.views import redirect_to_login
  5. from django.core.exceptions import ImproperlyConfigured, PermissionDenied
  6. from django.shortcuts import resolve_url
  7. class AccessMixin:
  8. """
  9. Abstract CBV mixin that gives access mixins the same customizable
  10. functionality.
  11. """
  12. login_url = None
  13. permission_denied_message = ''
  14. raise_exception = False
  15. redirect_field_name = REDIRECT_FIELD_NAME
  16. def get_login_url(self):
  17. """
  18. Override this method to override the login_url attribute.
  19. """
  20. login_url = self.login_url or settings.LOGIN_URL
  21. if not login_url:
  22. raise ImproperlyConfigured(
  23. '{0} is missing the login_url attribute. Define {0}.login_url, settings.LOGIN_URL, or override '
  24. '{0}.get_login_url().'.format(self.__class__.__name__)
  25. )
  26. return str(login_url)
  27. def get_permission_denied_message(self):
  28. """
  29. Override this method to override the permission_denied_message attribute.
  30. """
  31. return self.permission_denied_message
  32. def get_redirect_field_name(self):
  33. """
  34. Override this method to override the redirect_field_name attribute.
  35. """
  36. return self.redirect_field_name
  37. def handle_no_permission(self):
  38. if self.raise_exception or self.request.user.is_authenticated:
  39. raise PermissionDenied(self.get_permission_denied_message())
  40. path = self.request.build_absolute_uri()
  41. resolved_login_url = resolve_url(self.get_login_url())
  42. # If the login url is the same scheme and net location then use the
  43. # path as the "next" url.
  44. login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
  45. current_scheme, current_netloc = urlparse(path)[:2]
  46. if (
  47. (not login_scheme or login_scheme == current_scheme) and
  48. (not login_netloc or login_netloc == current_netloc)
  49. ):
  50. path = self.request.get_full_path()
  51. return redirect_to_login(
  52. path,
  53. resolved_login_url,
  54. self.get_redirect_field_name(),
  55. )
  56. class LoginRequiredMixin(AccessMixin):
  57. """Verify that the current user is authenticated."""
  58. def dispatch(self, request, *args, **kwargs):
  59. if not request.user.is_authenticated:
  60. return self.handle_no_permission()
  61. return super().dispatch(request, *args, **kwargs)
  62. class PermissionRequiredMixin(AccessMixin):
  63. """Verify that the current user has all specified permissions."""
  64. permission_required = None
  65. def get_permission_required(self):
  66. """
  67. Override this method to override the permission_required attribute.
  68. Must return an iterable.
  69. """
  70. if self.permission_required is None:
  71. raise ImproperlyConfigured(
  72. '{0} is missing the permission_required attribute. Define {0}.permission_required, or override '
  73. '{0}.get_permission_required().'.format(self.__class__.__name__)
  74. )
  75. if isinstance(self.permission_required, str):
  76. perms = (self.permission_required,)
  77. else:
  78. perms = self.permission_required
  79. return perms
  80. def has_permission(self):
  81. """
  82. Override this method to customize the way permissions are checked.
  83. """
  84. perms = self.get_permission_required()
  85. return self.request.user.has_perms(perms)
  86. def dispatch(self, request, *args, **kwargs):
  87. if not self.has_permission():
  88. return self.handle_no_permission()
  89. return super().dispatch(request, *args, **kwargs)
  90. class UserPassesTestMixin(AccessMixin):
  91. """
  92. Deny a request with a permission error if the test_func() method returns
  93. False.
  94. """
  95. def test_func(self):
  96. raise NotImplementedError(
  97. '{} is missing the implementation of the test_func() method.'.format(self.__class__.__name__)
  98. )
  99. def get_test_func(self):
  100. """
  101. Override this method to use a different test_func method.
  102. """
  103. return self.test_func
  104. def dispatch(self, request, *args, **kwargs):
  105. user_test_result = self.get_test_func()()
  106. if not user_test_result:
  107. return self.handle_no_permission()
  108. return super().dispatch(request, *args, **kwargs)