|
|
|
@ -35,6 +35,7 @@ from django.contrib import messages |
|
|
|
from django.shortcuts import redirect |
|
|
|
from django.urls import reverse |
|
|
|
from django.utils.translation import ugettext as _ |
|
|
|
from rest_framework.response import Response |
|
|
|
|
|
|
|
from re2o.utils import get_group_having_permission |
|
|
|
|
|
|
|
@ -60,7 +61,7 @@ def acl_error_message(msg, permissions): |
|
|
|
# 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 |
|
|
|
# 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 |
|
|
|
permission by calling model.method_name. If the flag on_instance is True, |
|
|
|
tries to get an instance of the model by calling |
|
|
|
@ -121,6 +122,9 @@ on_instance=False) |
|
|
|
method `get_instance` of the model, with the arguments originally |
|
|
|
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: |
|
|
|
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 |
|
|
|
@ -175,8 +179,7 @@ ModelC) |
|
|
|
# `wrapper` inside the `decorator` function, you need to read some |
|
|
|
# documentation on decorators ! |
|
|
|
def decorator(view): |
|
|
|
"""The decorator to use on a specific view |
|
|
|
""" |
|
|
|
"""The decorator to use on a specific view""" |
|
|
|
|
|
|
|
def wrapper(request, *args, **kwargs): |
|
|
|
"""The wrapper used for a specific request""" |
|
|
|
@ -243,23 +246,36 @@ ModelC) |
|
|
|
warning_messages.append(acl_error_message(msg, permissions)) |
|
|
|
|
|
|
|
# Display the warning messages |
|
|
|
if warning_messages: |
|
|
|
for msg in warning_messages: |
|
|
|
messages.warning(request, msg) |
|
|
|
if not api: |
|
|
|
if warning_messages: |
|
|
|
for msg in warning_messages: |
|
|
|
messages.warning(request, msg) |
|
|
|
|
|
|
|
# If there is any error message, then the request must be denied. |
|
|
|
if error_messages: |
|
|
|
# We display the message |
|
|
|
for msg in error_messages: |
|
|
|
messages.error( |
|
|
|
request, |
|
|
|
msg or _("You don't have the right to access this menu."), |
|
|
|
) |
|
|
|
if not api: |
|
|
|
for msg in error_messages: |
|
|
|
messages.error( |
|
|
|
request, |
|
|
|
msg or _("You don't have the right to access this menu."), |
|
|
|
) |
|
|
|
# And redirect the user to the right place. |
|
|
|
if request.user.id is not None: |
|
|
|
return redirect( |
|
|
|
reverse("users:profil", kwargs={"userid": str(request.user.id)}) |
|
|
|
) |
|
|
|
if not api: |
|
|
|
return redirect( |
|
|
|
reverse( |
|
|
|
"users:profil", kwargs={"userid": str(request.user.id)} |
|
|
|
) |
|
|
|
) |
|
|
|
else: |
|
|
|
return Response( |
|
|
|
data={ |
|
|
|
"errors": error_messages, |
|
|
|
"warning": warning_messages, |
|
|
|
}, |
|
|
|
status=403, |
|
|
|
) |
|
|
|
else: |
|
|
|
return redirect(reverse("index")) |
|
|
|
return view(request, *chain(instances, args), **kwargs) |
|
|
|
@ -310,12 +326,10 @@ def can_delete_set(model): |
|
|
|
If none of them, return an error""" |
|
|
|
|
|
|
|
def decorator(view): |
|
|
|
"""The decorator to use on a specific view |
|
|
|
""" |
|
|
|
"""The decorator to use on a specific view""" |
|
|
|
|
|
|
|
def wrapper(request, *args, **kwargs): |
|
|
|
"""The wrapper used for a specific request |
|
|
|
""" |
|
|
|
"""The wrapper used for a specific request""" |
|
|
|
all_objects = model.objects.all() |
|
|
|
instances_id = [] |
|
|
|
for instance in all_objects: |
|
|
|
@ -356,8 +370,7 @@ def can_view_all(*targets): |
|
|
|
|
|
|
|
|
|
|
|
def can_view_app(*apps_name): |
|
|
|
"""Decorator to check if an user can view the applications. |
|
|
|
""" |
|
|
|
"""Decorator to check if an user can view the applications.""" |
|
|
|
for app_name in apps_name: |
|
|
|
assert app_name in sys.modules.keys() |
|
|
|
return acl_base_decorator( |
|
|
|
@ -371,8 +384,7 @@ def can_edit_history(view): |
|
|
|
"""Decorator to check if an user can edit history.""" |
|
|
|
|
|
|
|
def wrapper(request, *args, **kwargs): |
|
|
|
"""The wrapper used for a specific request |
|
|
|
""" |
|
|
|
"""The wrapper used for a specific request""" |
|
|
|
if request.user.has_perm("admin.change_logentry"): |
|
|
|
return view(request, *args, **kwargs) |
|
|
|
messages.error(request, _("You don't have the right to edit the history.")) |
|
|
|
@ -381,3 +393,29 @@ def can_edit_history(view): |
|
|
|
) |
|
|
|
|
|
|
|
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) |
|
|
|
|