Merge branch 'bugfix' into 'master'

Fix huge permission problem inducing server memory leaks for etickets

See merge request ae/Sith!251
This commit is contained in:
Antoine Bartuccio 2019-10-17 12:36:57 +02:00
commit 1848945d64
5 changed files with 69 additions and 66 deletions

View File

@ -641,7 +641,7 @@ class MailingFormTest(TestCase):
{"action": MailingForm.ACTION_NEW_MAILING, "mailing_email": "mde"}, {"action": MailingForm.ACTION_NEW_MAILING, "mailing_email": "mde"},
) )
mde = Mailing.objects.get(email="mde") mde = Mailing.objects.get(email="mde")
response = self.client.post( self.client.post(
reverse("club:mailing", kwargs={"club_id": self.bdf.id}), reverse("club:mailing", kwargs={"club_id": self.bdf.id}),
{ {
"action": MailingForm.ACTION_NEW_SUBSCRIPTION, "action": MailingForm.ACTION_NEW_SUBSCRIPTION,
@ -650,6 +650,11 @@ class MailingFormTest(TestCase):
"subscription_mailing": mde.id, "subscription_mailing": mde.id,
}, },
) )
response = self.client.get(
reverse("club:mailing", kwargs={"club_id": self.bdf.id})
)
self.assertContains(response, "comunity@git.an") self.assertContains(response, "comunity@git.an")
self.assertContains(response, "richard@git.an") self.assertContains(response, "richard@git.an")
self.assertContains(response, "krophil@git.an") self.assertContains(response, "krophil@git.an")

View File

@ -574,7 +574,8 @@ class ClubMailingView(ClubTabsMixin, CanEditMixin, DetailFormView):
except ValidationError as validation_error: except ValidationError as validation_error:
return validation_error return validation_error
users_to_save.append(sub.save()) sub.save()
users_to_save.append(sub)
if cleaned_data["subscription_email"]: if cleaned_data["subscription_email"]:
sub = MailingSubscription( sub = MailingSubscription(

View File

@ -140,6 +140,52 @@ def can_view(obj, user):
return can_edit(obj, user) return can_edit(obj, user)
class GenericContentPermissionMixinBuilder(View):
"""
Used to build permission mixins
This view protect any child view that would be showing an object that is restricted based
on two properties
:prop permission_function: function to test permission with, takes an object and an user an return a bool
:prop raised_error: permission to be raised
:raises: raised_error
"""
permission_function = lambda obj, user: False
raised_error = PermissionDenied
@classmethod
def get_permission_function(cls, obj, user):
return cls.permission_function(obj, user)
def dispatch(self, request, *arg, **kwargs):
if hasattr(self, "get_object") and callable(self.get_object):
self.object = self.get_object()
if not self.get_permission_function(self.object, request.user):
raise self.raised_error
return super(GenericContentPermissionMixinBuilder, self).dispatch(
request, *arg, **kwargs
)
# If we get here, it's a ListView
queryset = self.get_queryset()
l_id = [o.id for o in queryset if self.get_permission_function(o, request.user)]
if not l_id and queryset.count() != 0:
raise self.raised_error
self._get_queryset = self.get_queryset
def get_qs(self2):
return self2._get_queryset().filter(id__in=l_id)
self.get_queryset = types.MethodType(get_qs, self)
return super(GenericContentPermissionMixinBuilder, self).dispatch(
request, *arg, **kwargs
)
class CanCreateMixin(View): class CanCreateMixin(View):
""" """
This view is made to protect any child view that would create an object, and thus, that can not be protected by any This view is made to protect any child view that would create an object, and thus, that can not be protected by any
@ -161,7 +207,7 @@ class CanCreateMixin(View):
raise PermissionDenied raise PermissionDenied
class CanEditPropMixin(View): class CanEditPropMixin(GenericContentPermissionMixinBuilder):
""" """
This view is made to protect any child view that would be showing some properties of an object that are restricted This view is made to protect any child view that would be showing some properties of an object that are restricted
to only the owner group of the given object. to only the owner group of the given object.
@ -171,28 +217,10 @@ class CanEditPropMixin(View):
:raises: PermissionDenied :raises: PermissionDenied
""" """
def dispatch(self, request, *arg, **kwargs): permission_function = can_edit_prop
try:
self.object = self.get_object()
if can_edit_prop(self.object, request.user):
return super(CanEditPropMixin, self).dispatch(request, *arg, **kwargs)
return forbidden(request)
except:
pass
# If we get here, it's a ListView
l_id = [o.id for o in self.get_queryset() if can_edit_prop(o, request.user)]
if not l_id and self.get_queryset().count() != 0:
raise PermissionDenied
self._get_queryset = self.get_queryset
def get_qs(self2):
return self2._get_queryset().filter(id__in=l_id)
self.get_queryset = types.MethodType(get_qs, self)
return super(CanEditPropMixin, self).dispatch(request, *arg, **kwargs)
class CanEditMixin(View): class CanEditMixin(GenericContentPermissionMixinBuilder):
""" """
This view makes exactly the same thing as its direct parent, but checks the group on the edit_groups field of the This view makes exactly the same thing as its direct parent, but checks the group on the edit_groups field of the
object object
@ -200,28 +228,10 @@ class CanEditMixin(View):
:raises: PermissionDenied :raises: PermissionDenied
""" """
def dispatch(self, request, *arg, **kwargs): permission_function = can_edit
try:
self.object = self.get_object()
if can_edit(self.object, request.user):
return super(CanEditMixin, self).dispatch(request, *arg, **kwargs)
return forbidden(request)
except:
pass
# If we get here, it's a ListView
l_id = [o.id for o in self.get_queryset() if can_edit(o, request.user)]
if not l_id and self.get_queryset().count() != 0:
raise PermissionDenied
self._get_queryset = self.get_queryset
def get_qs(self2):
return self2._get_queryset().filter(id__in=l_id)
self.get_queryset = types.MethodType(get_qs, self)
return super(CanEditMixin, self).dispatch(request, *arg, **kwargs)
class CanViewMixin(View): class CanViewMixin(GenericContentPermissionMixinBuilder):
""" """
This view still makes exactly the same thing as its direct parent, but checks the group on the view_groups field of This view still makes exactly the same thing as its direct parent, but checks the group on the view_groups field of
the object the object
@ -229,28 +239,7 @@ class CanViewMixin(View):
:raises: PermissionDenied :raises: PermissionDenied
""" """
def dispatch(self, request, *arg, **kwargs): permission_function = can_view
try:
self.object = self.get_object()
if can_view(self.object, request.user):
return super(CanViewMixin, self).dispatch(request, *arg, **kwargs)
return forbidden(request)
except:
pass
# If we get here, it's a ListView
queryset = self.get_queryset()
l_id = [o.id for o in queryset if can_view(o, request.user)]
if not l_id and queryset.count() != 0:
raise PermissionDenied
self._get_queryset = self.get_queryset
def get_qs(self2):
return self2._get_queryset().filter(id__in=l_id)
self.get_queryset = types.MethodType(get_qs, self)
return super(CanViewMixin, self).dispatch(request, *arg, **kwargs)
class FormerSubscriberMixin(View): class FormerSubscriberMixin(View):

View File

@ -478,6 +478,10 @@ class Selling(models.Model):
return user.is_owner(self.counter) and self.payment_method != "CARD" return user.is_owner(self.counter) and self.payment_method != "CARD"
def can_be_viewed_by(self, user): def can_be_viewed_by(self, user):
if (
not hasattr(self, "customer") or self.customer is None
): # Customer can be set to Null
return False
return user == self.customer.user return user == self.customer.user
def delete(self, *args, **kwargs): def delete(self, *args, **kwargs):

View File

@ -1752,7 +1752,11 @@ class EticketPDFView(CanViewMixin, DetailView):
from reportlab.graphics.barcode.qr import QrCodeWidget from reportlab.graphics.barcode.qr import QrCodeWidget
from reportlab.graphics import renderPDF from reportlab.graphics import renderPDF
self.object = self.get_object() if not (
hasattr(self.object, "product") and hasattr(self.object.product, "eticket")
):
raise Http404
eticket = self.object.product.eticket eticket = self.object.product.eticket
user = self.object.customer.user user = self.object.customer.user
code = "%s %s %s %s" % ( code = "%s %s %s %s" % (