You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

decorators.py 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. from functools import wraps
  2. from six.moves.urllib import parse
  3. from django.contrib.auth import REDIRECT_FIELD_NAME
  4. from django.contrib.auth.decorators import user_passes_test
  5. from django.shortcuts import render
  6. from django.contrib import messages
  7. from django.contrib.auth.views import redirect_to_login
  8. from django.core.exceptions import PermissionDenied
  9. from django.core.urlresolvers import reverse_lazy
  10. from django.utils.translation import ugettext_lazy as _
  11. def staff_member_required(view_func, login_url=None):
  12. """
  13. Ensure that the user is a logged-in staff member.
  14. * If not authenticated, redirect to a specified login URL.
  15. * If not staff, show a 403 page
  16. This decorator is based on the decorator with the same name from
  17. django.contrib.admin.views.decorators. This one is superior as it allows a
  18. redirect URL to be specified.
  19. """
  20. if login_url is None:
  21. login_url = reverse_lazy('customer:login')
  22. @wraps(view_func)
  23. def _checklogin(request, *args, **kwargs):
  24. if request.user.is_active and request.user.is_staff:
  25. return view_func(request, *args, **kwargs)
  26. # If user is not logged in, redirect to login page
  27. if not request.user.is_authenticated():
  28. # If the login url is the same scheme and net location then just
  29. # use the path as the "next" url.
  30. path = request.build_absolute_uri()
  31. login_scheme, login_netloc = parse.urlparse(login_url)[:2]
  32. current_scheme, current_netloc = parse.urlparse(path)[:2]
  33. if ((not login_scheme or login_scheme == current_scheme) and
  34. (not login_netloc or login_netloc == current_netloc)):
  35. path = request.get_full_path()
  36. messages.warning(request, _("You must log in to access this page"))
  37. return redirect_to_login(path, login_url, REDIRECT_FIELD_NAME)
  38. else:
  39. # User does not have permission to view this page
  40. raise PermissionDenied
  41. return _checklogin
  42. def check_permissions(user, permissions):
  43. """
  44. Permissions can be a list or a tuple of lists. If it is a tuple,
  45. every permission list will be evaluated and the outcome will be checked
  46. for truthiness.
  47. Each item of the list(s) must be either a valid Django permission name
  48. (model.codename) or a property or method on the User model
  49. (e.g. 'is_active', 'is_superuser').
  50. Example usage:
  51. - permissions_required(['is_staff', ])
  52. would replace staff_member_required
  53. - permissions_required(['is_anonymous', ])
  54. would replace login_forbidden
  55. - permissions_required((['is_staff',], ['partner.dashboard_access']))
  56. allows both staff users and users with the above permission
  57. """
  58. def _check_one_permission_list(perms):
  59. regular_permissions = [perm for perm in perms if '.' in perm]
  60. conditions = [perm for perm in perms if '.' not in perm]
  61. # always check for is_active if not checking for is_anonymous
  62. if (conditions and
  63. 'is_anonymous' not in conditions and
  64. 'is_active' not in conditions):
  65. conditions.append('is_active')
  66. attributes = [getattr(user, perm) for perm in conditions]
  67. # evaluates methods, explicitly casts properties to booleans
  68. passes_conditions = all([
  69. attr() if callable(attr) else bool(attr) for attr in attributes])
  70. return passes_conditions and user.has_perms(regular_permissions)
  71. if not permissions:
  72. return True
  73. elif isinstance(permissions, list):
  74. return _check_one_permission_list(permissions)
  75. else:
  76. return any(_check_one_permission_list(perm) for perm in permissions)
  77. def permissions_required(permissions, login_url=None):
  78. """
  79. Decorator that checks if a user has the given permissions.
  80. Accepts a list or tuple of lists of permissions (see check_permissions
  81. documentation).
  82. If the user is not logged in and the test fails, she is redirected to a
  83. login page. If the user is logged in, she gets a HTTP 403 Permission Denied
  84. message, analogous to Django's permission_required decorator.
  85. """
  86. if login_url is None:
  87. login_url = reverse_lazy('customer:login')
  88. def _check_permissions(user):
  89. outcome = check_permissions(user, permissions)
  90. if not outcome and user.is_authenticated():
  91. raise PermissionDenied
  92. else:
  93. return outcome
  94. return user_passes_test(_check_permissions, login_url=login_url)
  95. def login_forbidden(view_func, template_name='login_forbidden.html',
  96. status=403):
  97. """
  98. Only allow anonymous users to access this view.
  99. """
  100. @wraps(view_func)
  101. def _checklogin(request, *args, **kwargs):
  102. if not request.user.is_authenticated():
  103. return view_func(request, *args, **kwargs)
  104. return render(request, template_name, status=status)
  105. return _checklogin