The memoization doesn't work when objects don't have a primary key.

This commit is contained in:
Yohann D'ANELLO 2020-04-02 14:50:28 +02:00
parent 8ad464ae0c
commit be42801709
7 changed files with 28 additions and 14 deletions

View File

@ -61,7 +61,7 @@ class MembershipTable(tables.Table):
def render_club(self, value): def render_club(self, value):
s = value.name s = value.name
if PermissionBackend().has_perm(get_current_authenticated_user(), "member.view_club", value): if PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_club", value):
s = format_html("<a href={url}>{name}</a>", s = format_html("<a href={url}>{name}</a>",
url=reverse_lazy('member:club_detail', kwargs={"pk": value.pk}), name=s) url=reverse_lazy('member:club_detail', kwargs={"pk": value.pk}), name=s)
@ -86,7 +86,7 @@ class MembershipTable(tables.Table):
date_end=datetime.now().date(), date_end=datetime.now().date(),
fee=0, fee=0,
) )
if PermissionBackend().has_perm(get_current_authenticated_user(), if PermissionBackend.check_perm(get_current_authenticated_user(),
"member:add_membership", empty_membership): # If the user has right "member:add_membership", empty_membership): # If the user has right
t = format_html(t + ' <a class="btn btn-warning" href="{url}">{text}</a>', t = format_html(t + ' <a class="btn btn-warning" href="{url}">{text}</a>',
url=reverse_lazy('member:club_renew_membership', url=reverse_lazy('member:club_renew_membership',
@ -96,7 +96,7 @@ class MembershipTable(tables.Table):
def render_roles(self, record): def render_roles(self, record):
roles = record.roles.all() roles = record.roles.all()
s = ", ".join(str(role) for role in roles) s = ", ".join(str(role) for role in roles)
if PermissionBackend().has_perm(get_current_authenticated_user(), "member.change_membership_roles", record): if PermissionBackend.check_perm(get_current_authenticated_user(), "member.change_membership_roles", record):
s = format_html("<a href='" + str(reverse_lazy("member:club_manage_roles", kwargs={"pk": record.pk})) s = format_html("<a href='" + str(reverse_lazy("member:club_manage_roles", kwargs={"pk": record.pk}))
+ "'>" + s + "</a>") + "'>" + s + "</a>")
return s return s

View File

@ -299,7 +299,7 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
club = context["club"] club = context["club"]
if PermissionBackend().has_perm(self.request.user, "member.change_club_membership_start", club): if PermissionBackend.check_perm(self.request.user, "member.change_club_membership_start", club):
club.update_membership_dates() club.update_membership_dates()
club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))\ club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))\

View File

@ -114,8 +114,16 @@ class PermissionBackend(ModelBackend):
query = query | perm.query query = query | perm.query
return query return query
@staticmethod
@memoize @memoize
def has_perm(self, user_obj, perm, obj=None): def check_perm(user_obj, perm, obj=None):
"""
Check is the given user has the permission over a given object.
The result is then memoized.
Exception: for add permissions, since the object is not hashable since it doesn't have any
primary key, the result is not memoized. Moreover, the right could change
(e.g. for a transaction, the balance of the user could change)
"""
if user_obj is None or isinstance(user_obj, AnonymousUser): if user_obj is None or isinstance(user_obj, AnonymousUser):
return False return False
@ -134,10 +142,13 @@ class PermissionBackend(ModelBackend):
perm_field = perm[2] if len(perm) == 3 else None perm_field = perm[2] if len(perm) == 3 else None
ct = ContentType.objects.get_for_model(obj) ct = ContentType.objects.get_for_model(obj)
if any(permission.applies(obj, perm_type, perm_field) if any(permission.applies(obj, perm_type, perm_field)
for permission in self.permissions(user_obj, ct, perm_type)): for permission in PermissionBackend.permissions(user_obj, ct, perm_type)):
return True return True
return False return False
def has_perm(self, user_obj, perm, obj=None):
return PermissionBackend.check_perm(user_obj, perm, obj)
def has_module_perms(self, user_obj, app_label): def has_module_perms(self, user_obj, app_label):
return False return False

View File

@ -49,7 +49,10 @@ def memoize(f):
# lru_cache makes the job of memoization # lru_cache makes the job of memoization
# We store only the 512 latest data per session. It has to be enough. # We store only the 512 latest data per session. It has to be enough.
sess_funs[sess_key] = lru_cache(512)(f) sess_funs[sess_key] = lru_cache(512)(f)
return sess_funs[sess_key](*args, **kwargs) try:
return sess_funs[sess_key](*args, **kwargs)
except TypeError: # For add permissions, objects are not hashable (not yet created). Don't memoize this case.
return f(*args, **kwargs)
func.func_name = f.__name__ func.func_name = f.__name__

View File

@ -44,7 +44,7 @@ class StrongDjangoObjectPermissions(DjangoObjectPermissions):
perms = self.get_required_object_permissions(request.method, model_cls) perms = self.get_required_object_permissions(request.method, model_cls)
# if not user.has_perms(perms, obj): # if not user.has_perms(perms, obj):
if not all(PermissionBackend().has_perm(user, perm, obj) for perm in perms): if not all(PermissionBackend.check_perm(user, perm, obj) for perm in perms):
# If the user does not have permissions we need to determine if # If the user does not have permissions we need to determine if
# they have read permissions to see 403, or not, and simply see # they have read permissions to see 403, or not, and simply see
# a 404 response. # a 404 response.

View File

@ -44,7 +44,7 @@ def pre_save_object(sender, instance, **kwargs):
# We check if the user can change the model # We check if the user can change the model
# If the user has all right on a model, then OK # If the user has all right on a model, then OK
if PermissionBackend().has_perm(user, app_label + ".change_" + model_name, instance): if PermissionBackend.check_perm(user, app_label + ".change_" + model_name, instance):
return return
# In the other case, we check if he/she has the right to change one field # In the other case, we check if he/she has the right to change one field
@ -56,11 +56,11 @@ def pre_save_object(sender, instance, **kwargs):
# If the field wasn't modified, no need to check the permissions # If the field wasn't modified, no need to check the permissions
if old_value == new_value: if old_value == new_value:
continue continue
if not PermissionBackend().has_perm(user, app_label + ".change_" + model_name + "_" + field_name, instance): if not PermissionBackend.check_perm(user, app_label + ".change_" + model_name + "_" + field_name, instance):
raise PermissionDenied raise PermissionDenied
else: else:
# We check if the user has right to add the object # We check if the user has right to add the object
has_perm = PermissionBackend().has_perm(user, app_label + ".add_" + model_name, instance) has_perm = PermissionBackend.check_perm(user, app_label + ".add_" + model_name, instance)
if not has_perm: if not has_perm:
raise PermissionDenied raise PermissionDenied
@ -87,5 +87,5 @@ def pre_delete_object(instance, **kwargs):
model_name = model_name_full[1] model_name = model_name_full[1]
# We check if the user has rights to delete the object # We check if the user has rights to delete the object
if not PermissionBackend().has_perm(user, app_label + ".delete_" + model_name, instance): if not PermissionBackend.check_perm(user, app_label + ".delete_" + model_name, instance):
raise PermissionDenied raise PermissionDenied

View File

@ -54,7 +54,7 @@ def model_list(model_name, t="view"):
def has_perm(perm, obj): def has_perm(perm, obj):
return PermissionBackend().has_perm(get_current_authenticated_user(), perm, obj) return PermissionBackend.check_perm(get_current_authenticated_user(), perm, obj)
def can_create_transaction(): def can_create_transaction():
@ -77,7 +77,7 @@ def can_create_transaction():
amount=0, amount=0,
reason="Check permissions", reason="Check permissions",
) )
session["can_create_transaction"] = PermissionBackend().has_perm(user, "note.add_transaction", empty_transaction) session["can_create_transaction"] = PermissionBackend.check_perm(user, "note.add_transaction", empty_transaction)
return session.get("can_create_transaction") == 1 return session.get("can_create_transaction") == 1