remove CanCreateMixin usage from election

This commit is contained in:
imperosol 2025-03-16 23:19:00 +01:00
parent a74037a7f3
commit 86353dca04
2 changed files with 102 additions and 66 deletions

View File

@ -1,7 +1,10 @@
from datetime import timedelta
import pytest import pytest
from django.conf import settings from django.conf import settings
from django.test import TestCase from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import now
from model_bakery import baker from model_bakery import baker
from core.baker_recipes import subscriber_user from core.baker_recipes import subscriber_user
@ -49,6 +52,27 @@ class TestElectionUpdateView(TestElection):
assert response.status_code == 403 assert response.status_code == 403
@pytest.mark.django_db
def test_election_create_list_permission(client: Client):
election = baker.make(Election, end_candidature=now() + timedelta(hours=1))
groups = [
Group.objects.get(pk=settings.SITH_GROUP_SUBSCRIBERS_ID),
baker.make(Group),
]
election.candidature_groups.add(groups[0])
election.edit_groups.add(groups[1])
url = reverse("election:create_list", kwargs={"election_id": election.id})
for user in subscriber_user.make(), baker.make(User, groups=[groups[1]]):
client.force_login(user)
assert client.get(url).status_code == 200
# the post is a 200 instead of a 302, because we don't give form data,
# but we don't care as we only test permissions here
assert client.post(url).status_code == 200
client.force_login(baker.make(User))
assert client.get(url).status_code == 403
assert client.post(url).status_code == 403
@pytest.mark.django_db @pytest.mark.django_db
def test_election_results(): def test_election_results():
election = baker.make( election = baker.make(

View File

@ -1,6 +1,12 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from cryptography.utils import cached_property
from django.conf import settings
from django.contrib.auth.mixins import (
LoginRequiredMixin,
PermissionRequiredMixin,
UserPassesTestMixin,
)
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.db import transaction from django.db import transaction
from django.db.models import QuerySet from django.db.models import QuerySet
@ -9,7 +15,7 @@ from django.urls import reverse, reverse_lazy
from django.views.generic import DetailView, ListView from django.views.generic import DetailView, ListView
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from core.auth.mixins import CanCreateMixin, CanEditMixin, CanViewMixin from core.auth.mixins import CanEditMixin, CanViewMixin
from election.forms import ( from election.forms import (
CandidateForm, CandidateForm,
ElectionForm, ElectionForm,
@ -94,15 +100,26 @@ class ElectionDetailView(CanViewMixin, DetailView):
# Form view # Form view
class VoteFormView(CanCreateMixin, FormView): class VoteFormView(LoginRequiredMixin, UserPassesTestMixin, FormView):
"""Alows users to vote.""" """Alows users to vote."""
form_class = VoteForm form_class = VoteForm
template_name = "election/election_detail.jinja" template_name = "election/election_detail.jinja"
def dispatch(self, request, *arg, **kwargs): @cached_property
self.election = get_object_or_404(Election, pk=kwargs["election_id"]) def election(self):
return super().dispatch(request, *arg, **kwargs) return get_object_or_404(Election, pk=self.kwargs["election_id"])
def test_func(self):
groups = set(self.election.vote_groups.values_list("id", flat=True))
if (
settings.SITH_GROUP_SUBSCRIBERS_ID in groups
and self.request.user.is_subscribed
):
# the subscriber group isn't truly attached to users,
# so it must be dealt with separately
return True
return self.request.user.groups.filter(id__in=groups).exists()
def vote(self, election_data): def vote(self, election_data):
with transaction.atomic(): with transaction.atomic():
@ -122,20 +139,16 @@ class VoteFormView(CanCreateMixin, FormView):
self.election.voters.add(self.request.user) self.election.voters.add(self.request.user)
def get_form_kwargs(self): def get_form_kwargs(self):
kwargs = super().get_form_kwargs() return super().get_form_kwargs() | {
kwargs["election"] = self.election "election": self.election,
kwargs["user"] = self.request.user "user": self.request.user,
return kwargs }
def form_valid(self, form): def form_valid(self, form):
"""Verify that the user is part in a vote group.""" """Verify that the user is part in a vote group."""
data = form.clean() data = form.clean()
res = super(FormView, self).form_valid(form) self.vote(data)
for grp_id in self.election.vote_groups.values_list("pk", flat=True): return super().form_valid(form)
if self.request.user.is_in_group(pk=grp_id):
self.vote(data)
return res
return res
def get_success_url(self, **kwargs): def get_success_url(self, **kwargs):
return reverse_lazy("election:detail", kwargs={"election_id": self.election.id}) return reverse_lazy("election:detail", kwargs={"election_id": self.election.id})
@ -200,80 +213,79 @@ class ElectionCreateView(PermissionRequiredMixin, CreateView):
return reverse("election:detail", kwargs={"election_id": self.object.id}) return reverse("election:detail", kwargs={"election_id": self.object.id})
class RoleCreateView(CanCreateMixin, CreateView): class RoleCreateView(LoginRequiredMixin, UserPassesTestMixin, CreateView):
model = Role model = Role
form_class = RoleForm form_class = RoleForm
template_name = "core/create.jinja" template_name = "core/create.jinja"
def dispatch(self, request, *arg, **kwargs): @cached_property
self.election = get_object_or_404(Election, pk=kwargs["election_id"]) def election(self):
return get_object_or_404(Election, pk=self.kwargs["election_id"])
def test_func(self):
if not self.election.is_vote_editable: if not self.election.is_vote_editable:
raise PermissionDenied return False
return super().dispatch(request, *arg, **kwargs) if self.request.user.has_perm("election.add_role"):
return True
groups = set(self.election.edit_groups.values_list("id", flat=True))
if (
settings.SITH_GROUP_SUBSCRIBERS_ID in groups
and self.request.user.is_subscribed
):
# the subscriber group isn't truly attached to users,
# so it must be dealt with separately
return True
return self.request.user.groups.filter(id__in=groups).exists()
def get_initial(self): def get_initial(self):
init = {} return {"election": self.election}
init["election"] = self.election
return init
def form_valid(self, form):
"""Verify that the user can edit properly."""
obj: Role = form.instance
user: User = self.request.user
if obj.election:
for grp_id in obj.election.edit_groups.values_list("pk", flat=True):
if user.is_in_group(pk=grp_id):
return super(CreateView, self).form_valid(form)
raise PermissionDenied
def get_form_kwargs(self): def get_form_kwargs(self):
kwargs = super().get_form_kwargs() return super().get_form_kwargs() | {"election_id": self.election.id}
kwargs["election_id"] = self.election.id
return kwargs
def get_success_url(self, **kwargs): def get_success_url(self, **kwargs):
return reverse_lazy( return reverse(
"election:detail", kwargs={"election_id": self.object.election.id} "election:detail", kwargs={"election_id": self.object.election_id}
) )
class ElectionListCreateView(CanCreateMixin, CreateView): class ElectionListCreateView(LoginRequiredMixin, UserPassesTestMixin, CreateView):
model = ElectionList model = ElectionList
form_class = ElectionListForm form_class = ElectionListForm
template_name = "core/create.jinja" template_name = "core/create.jinja"
def dispatch(self, request, *arg, **kwargs): @cached_property
self.election = get_object_or_404(Election, pk=kwargs["election_id"]) def election(self):
return get_object_or_404(Election, pk=self.kwargs["election_id"])
def test_func(self):
if not self.election.is_vote_editable: if not self.election.is_vote_editable:
raise PermissionDenied return False
return super().dispatch(request, *arg, **kwargs) if self.request.user.has_perm("election.add_electionlist"):
return True
groups = set(
self.election.candidature_groups.values("id")
.union(self.election.edit_groups.values("id"))
.values_list("id", flat=True)
)
if (
settings.SITH_GROUP_SUBSCRIBERS_ID in groups
and self.request.user.is_subscribed
):
# the subscriber group isn't truly attached to users,
# so it must be dealt with separately
return True
return self.request.user.groups.filter(id__in=groups).exists()
def get_initial(self): def get_initial(self):
init = {} return {"election": self.election}
init["election"] = self.election
return init
def get_form_kwargs(self): def get_form_kwargs(self):
kwargs = super().get_form_kwargs() return super().get_form_kwargs() | {"election_id": self.election.id}
kwargs["election_id"] = self.election.id
return kwargs
def form_valid(self, form):
"""Verify that the user can vote on this election."""
obj: ElectionList = form.instance
user: User = self.request.user
if obj.election:
for grp_id in obj.election.candidature_groups.values_list("pk", flat=True):
if user.is_in_group(pk=grp_id):
return super(CreateView, self).form_valid(form)
for grp_id in obj.election.edit_groups.values_list("pk", flat=True):
if user.is_in_group(pk=grp_id):
return super(CreateView, self).form_valid(form)
raise PermissionDenied
def get_success_url(self, **kwargs): def get_success_url(self, **kwargs):
return reverse_lazy( return reverse(
"election:detail", kwargs={"election_id": self.object.election.id} "election:detail", kwargs={"election_id": self.object.election_id}
) )