|
|
@ -35,6 +35,7 @@ from django.contrib import messages |
|
|
from django.shortcuts import redirect |
|
|
from django.shortcuts import redirect |
|
|
from django.urls import reverse |
|
|
from django.urls import reverse |
|
|
from django.utils.translation import ugettext as _ |
|
|
from django.utils.translation import ugettext as _ |
|
|
|
|
|
from rest_framework.response import Response |
|
|
|
|
|
|
|
|
from re2o.utils import get_group_having_permission |
|
|
from re2o.utils import get_group_having_permission |
|
|
|
|
|
|
|
|
@ -43,11 +44,13 @@ def acl_error_message(msg, permissions): |
|
|
"""Create an error message for msg and permissions.""" |
|
|
"""Create an error message for msg and permissions.""" |
|
|
if permissions is None: |
|
|
if permissions is None: |
|
|
return msg |
|
|
return msg |
|
|
groups = ", ".join([g.name for g in get_group_having_permission(*permissions)]) |
|
|
groups = ", ".join( |
|
|
|
|
|
[g.name for g in get_group_having_permission(*permissions)]) |
|
|
message = msg or _("You don't have the right to edit this option.") |
|
|
message = msg or _("You don't have the right to edit this option.") |
|
|
if groups: |
|
|
if groups: |
|
|
return ( |
|
|
return ( |
|
|
message + _("You need to be a member of one of these groups: %s.") % groups |
|
|
message + |
|
|
|
|
|
_("You need to be a member of one of these groups: %s.") % groups |
|
|
) |
|
|
) |
|
|
else: |
|
|
else: |
|
|
return message + _("No group has the %s permission(s)!") % " or ".join( |
|
|
return message + _("No group has the %s permission(s)!") % " or ".join( |
|
|
@ -60,7 +63,7 @@ def acl_error_message(msg, permissions): |
|
|
# This is the function of main interest of this file. Almost all the decorators |
|
|
# This is the function of main interest of this file. Almost all the decorators |
|
|
# use it, and it is a fairly complicated piece of code. Let me guide you through |
|
|
# use it, and it is a fairly complicated piece of code. Let me guide you through |
|
|
# this ! 🌈😸 |
|
|
# this ! 🌈😸 |
|
|
def acl_base_decorator(method_name, *targets, on_instance=True): |
|
|
def acl_base_decorator(method_name, *targets, on_instance=True, api=False): |
|
|
"""Base decorator for acl. It checks if the `request.user` has the |
|
|
"""Base decorator for acl. It checks if the `request.user` has the |
|
|
permission by calling model.method_name. If the flag on_instance is True, |
|
|
permission by calling model.method_name. If the flag on_instance is True, |
|
|
tries to get an instance of the model by calling |
|
|
tries to get an instance of the model by calling |
|
|
@ -121,6 +124,9 @@ on_instance=False) |
|
|
method `get_instance` of the model, with the arguments originally |
|
|
method `get_instance` of the model, with the arguments originally |
|
|
passed to the view. |
|
|
passed to the view. |
|
|
|
|
|
|
|
|
|
|
|
api: when set to True, errors will no longer trigger redirection and |
|
|
|
|
|
messages but will send a 403 with errors in JSON |
|
|
|
|
|
|
|
|
Returns: |
|
|
Returns: |
|
|
The user is either redirected to their own page with an explanation |
|
|
The user is either redirected to their own page with an explanation |
|
|
message if at least one access is not granted, or to the view. In order |
|
|
message if at least one access is not granted, or to the view. In order |
|
|
@ -192,7 +198,8 @@ ModelC) |
|
|
# and store it to pass it to the view. |
|
|
# and store it to pass it to the view. |
|
|
if on_instance: |
|
|
if on_instance: |
|
|
try: |
|
|
try: |
|
|
target = target.get_instance(target_id, *args, **kwargs) |
|
|
target = target.get_instance( |
|
|
|
|
|
target_id, *args, **kwargs) |
|
|
instances.append(target) |
|
|
instances.append(target) |
|
|
except target.DoesNotExist: |
|
|
except target.DoesNotExist: |
|
|
# A non existing instance is a valid reason to deny |
|
|
# A non existing instance is a valid reason to deny |
|
|
@ -238,28 +245,37 @@ ModelC) |
|
|
# Store the messages at the right place. |
|
|
# Store the messages at the right place. |
|
|
for can, msg, permissions in process_target(target, fields, target_id): |
|
|
for can, msg, permissions in process_target(target, fields, target_id): |
|
|
if not can: |
|
|
if not can: |
|
|
error_messages.append(acl_error_message(msg, permissions)) |
|
|
error_messages.append( |
|
|
|
|
|
acl_error_message(msg, permissions)) |
|
|
elif msg: |
|
|
elif msg: |
|
|
warning_messages.append(acl_error_message(msg, permissions)) |
|
|
warning_messages.append( |
|
|
|
|
|
acl_error_message(msg, permissions)) |
|
|
|
|
|
|
|
|
# Display the warning messages |
|
|
# Display the warning messages |
|
|
if warning_messages: |
|
|
if not api: |
|
|
for msg in warning_messages: |
|
|
if warning_messages: |
|
|
messages.warning(request, msg) |
|
|
for msg in warning_messages: |
|
|
|
|
|
messages.warning(request, msg) |
|
|
|
|
|
|
|
|
# If there is any error message, then the request must be denied. |
|
|
# If there is any error message, then the request must be denied. |
|
|
if error_messages: |
|
|
if error_messages: |
|
|
# We display the message |
|
|
# We display the message |
|
|
for msg in error_messages: |
|
|
if not api: |
|
|
messages.error( |
|
|
for msg in error_messages: |
|
|
request, |
|
|
messages.error( |
|
|
msg or _("You don't have the right to access this menu."), |
|
|
request, |
|
|
) |
|
|
msg or _( |
|
|
|
|
|
"You don't have the right to access this menu."), |
|
|
|
|
|
) |
|
|
# And redirect the user to the right place. |
|
|
# And redirect the user to the right place. |
|
|
if request.user.id is not None: |
|
|
if request.user.id is not None: |
|
|
return redirect( |
|
|
if not api: |
|
|
reverse("users:profil", kwargs={"userid": str(request.user.id)}) |
|
|
return redirect( |
|
|
) |
|
|
reverse("users:profil", kwargs={ |
|
|
|
|
|
"userid": str(request.user.id)}) |
|
|
|
|
|
) |
|
|
|
|
|
else: |
|
|
|
|
|
return Response(data={"errors": error_messages, "warning": warning_messages}, status=403) |
|
|
else: |
|
|
else: |
|
|
return redirect(reverse("index")) |
|
|
return redirect(reverse("index")) |
|
|
return view(request, *chain(instances, args), **kwargs) |
|
|
return view(request, *chain(instances, args), **kwargs) |
|
|
@ -328,7 +344,8 @@ def can_delete_set(model): |
|
|
request, _("You don't have the right to access this menu.") |
|
|
request, _("You don't have the right to access this menu.") |
|
|
) |
|
|
) |
|
|
return redirect( |
|
|
return redirect( |
|
|
reverse("users:profil", kwargs={"userid": str(request.user.id)}) |
|
|
reverse("users:profil", kwargs={ |
|
|
|
|
|
"userid": str(request.user.id)}) |
|
|
) |
|
|
) |
|
|
return view(request, instances, *args, **kwargs) |
|
|
return view(request, instances, *args, **kwargs) |
|
|
|
|
|
|
|
|
@ -375,9 +392,36 @@ def can_edit_history(view): |
|
|
""" |
|
|
""" |
|
|
if request.user.has_perm("admin.change_logentry"): |
|
|
if request.user.has_perm("admin.change_logentry"): |
|
|
return view(request, *args, **kwargs) |
|
|
return view(request, *args, **kwargs) |
|
|
messages.error(request, _("You don't have the right to edit the history.")) |
|
|
messages.error(request, _( |
|
|
|
|
|
"You don't have the right to edit the history.")) |
|
|
return redirect( |
|
|
return redirect( |
|
|
reverse("users:profil", kwargs={"userid": str(request.user.id)}) |
|
|
reverse("users:profil", kwargs={"userid": str(request.user.id)}) |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
return wrapper |
|
|
return wrapper |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def can_view_all_api(*models): |
|
|
|
|
|
"""Decorator to check if an user can see an api page |
|
|
|
|
|
Only used on functionnal api views (class-based api views ACL are checked |
|
|
|
|
|
in api/permissions.py) |
|
|
|
|
|
""" |
|
|
|
|
|
return acl_base_decorator("can_view_all", *models, on_instance=False, api=True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def can_edit_all_api(*models): |
|
|
|
|
|
"""Decorator to check if an user can edit via the api |
|
|
|
|
|
We do not always know which instances will be edited, so we may need to know |
|
|
|
|
|
if the user can edit any instance. |
|
|
|
|
|
Only used on functionnal api views (class-based api views ACL are checked |
|
|
|
|
|
in api/permissions.py) |
|
|
|
|
|
""" |
|
|
|
|
|
return acl_base_decorator("can_edit_all", *models, on_instance=False, api=True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def can_create_api(*models): |
|
|
|
|
|
"""Decorator to check if an user can create the given models. via the api |
|
|
|
|
|
Only used on functionnal api views (class-based api views ACL are checked |
|
|
|
|
|
in api/permissions.py) |
|
|
|
|
|
""" |
|
|
|
|
|
return acl_base_decorator("can_create", *models, on_instance=False, api=True) |
|
|
|