removed race condition & cleanup

This commit is contained in:
Joshua Sangmeister 2020-04-06 16:38:07 +02:00 committed by Emanuel Schütze
parent 7882ea1a25
commit b7b8620153
5 changed files with 30 additions and 38 deletions

View File

@ -42,6 +42,6 @@ export class User extends BaseDecimalModel<User> {
} }
protected getDecimalFields(): string[] { protected getDecimalFields(): string[] {
return ["vote_weight"]; return ['vote_weight'];
} }
} }

View File

@ -2,7 +2,6 @@ from decimal import Decimal
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db import transaction from django.db import transaction
from django.db.utils import IntegrityError
from openslides.poll.views import BaseOptionViewSet, BasePollViewSet, BaseVoteViewSet from openslides.poll.views import BaseOptionViewSet, BasePollViewSet, BaseVoteViewSet
from openslides.utils.auth import has_perm from openslides.utils.auth import has_perm
@ -526,17 +525,11 @@ class AssignmentPollViewSet(BasePollViewSet):
poll.voted.add(check_user) poll.voted.add(check_user)
def add_user_to_voted_array(self, user, poll):
VotedModel = AssignmentPoll.voted.through
VotedModel.objects.create(assignmentpoll=poll, user=user)
def handle_named_vote(self, data, poll, user): def handle_named_vote(self, data, poll, user):
try:
with transaction.atomic():
return self.try_handle_named_vote(data, poll, user)
except IntegrityError:
raise ValidationError({"detail": "You have already voted"})
def try_handle_named_vote(self, data, poll, user):
if user in poll.voted.all():
raise ValidationError({"detail": "You have already voted"})
if poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES: if poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES:
self.create_votes_type_votes(data, poll, user) self.create_votes_type_votes(data, poll, user)
elif poll.pollmethod in ( elif poll.pollmethod in (
@ -546,16 +539,6 @@ class AssignmentPollViewSet(BasePollViewSet):
self.create_votes_type_named_pseudoanonymous(data, poll, user, user) self.create_votes_type_named_pseudoanonymous(data, poll, user, user)
def handle_pseudoanonymous_vote(self, data, poll, user): def handle_pseudoanonymous_vote(self, data, poll, user):
try:
with transaction.atomic():
return self.try_handle_pseudoanonymous_vote(data, poll, user)
except IntegrityError:
raise ValidationError({"detail": "You have already voted"})
def try_handle_pseudoanonymous_vote(self, data, poll, user):
if user in poll.voted.all():
raise ValidationError({"detail": "You have already voted"})
if poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES: if poll.pollmethod == AssignmentPoll.POLLMETHOD_VOTES:
self.create_votes_type_votes(data, poll, user) self.create_votes_type_votes(data, poll, user)

View File

@ -1176,6 +1176,10 @@ class MotionPollViewSet(BasePollViewSet):
return result return result
def add_user_to_voted_array(self, user, poll):
VotedModel = MotionPoll.voted.through
VotedModel.objects.create(motionpoll=poll, user=user)
def handle_analog_vote(self, data, poll, user): def handle_analog_vote(self, data, poll, user):
option = poll.options.get() option = poll.options.get()
vote, _ = MotionVote.objects.get_or_create(option=option, value="Y") vote, _ = MotionVote.objects.get_or_create(option=option, value="Y")
@ -1223,21 +1227,11 @@ class MotionPollViewSet(BasePollViewSet):
raise ValidationError("Data must be Y or N") raise ValidationError("Data must be Y or N")
def handle_named_vote(self, data, poll, user): def handle_named_vote(self, data, poll, user):
if user in poll.voted.all():
raise ValidationError({"detail": "You have already voted"})
poll.voted.add(user)
poll.save()
option = poll.options.get() option = poll.options.get()
vote = MotionVote.objects.create(user=user, option=option) vote = MotionVote.objects.create(user=user, option=option)
self.handle_named_and_pseudoanonymous_vote(data, user, poll, option, vote) self.handle_named_and_pseudoanonymous_vote(data, user, poll, option, vote)
def handle_pseudoanonymous_vote(self, data, poll, user): def handle_pseudoanonymous_vote(self, data, poll, user):
if user in poll.voted.all():
raise ValidationError({"detail": "You have already voted"})
poll.voted.add(user)
poll.save()
option = poll.options.get() option = poll.options.get()
vote = MotionVote.objects.create(user=None, option=option) vote = MotionVote.objects.create(user=None, option=option)
self.handle_named_and_pseudoanonymous_vote(data, user, poll, option, vote) self.handle_named_and_pseudoanonymous_vote(data, user, poll, option, vote)

View File

@ -2,6 +2,7 @@ from textwrap import dedent
from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import AnonymousUser
from django.db import transaction from django.db import transaction
from django.db.utils import IntegrityError
from rest_framework import status from rest_framework import status
from openslides.utils.auth import in_some_groups from openslides.utils.auth import in_some_groups
@ -184,6 +185,7 @@ class BasePollViewSet(ModelViewSet):
return Response() return Response()
@detail_route(methods=["POST"]) @detail_route(methods=["POST"])
@transaction.atomic
def vote(self, request, pk): def vote(self, request, pk):
""" """
For motion polls: Just "Y", "N" or "A" (if pollmethod is "YNA") For motion polls: Just "Y", "N" or "A" (if pollmethod is "YNA")
@ -219,10 +221,10 @@ class BasePollViewSet(ModelViewSet):
def assert_can_vote(self, poll, request): def assert_can_vote(self, poll, request):
""" """
Raises a permission denied, if the user is not allowed to vote. Raises a permission denied, if the user is not allowed to vote (or has already voted).
Adds the user to the voted array, so this needs to be reverted on error!
Analog: has to have manage permissions Analog: has to have manage permissions
Named & Pseudoanonymous: has to be in a poll group and present Named & Pseudoanonymous: has to be in a poll group and present
Note: For pseudoanonymous it is *not* tested, if the user has already voted!
""" """
if poll.type == BasePoll.TYPE_ANALOG: if poll.type == BasePoll.TYPE_ANALOG:
if not self.has_manage_permissions(): if not self.has_manage_permissions():
@ -231,11 +233,17 @@ class BasePollViewSet(ModelViewSet):
if poll.state != BasePoll.STATE_STARTED: if poll.state != BasePoll.STATE_STARTED:
raise ValidationError("You can only vote on a started poll.") raise ValidationError("You can only vote on a started poll.")
if not request.user.is_present or not in_some_groups( if not request.user.is_present or not in_some_groups(
request.user.id, request.user.id,
list(poll.groups.values_list("pk", flat=True)), list(poll.groups.values_list("pk", flat=True)),
exact=True, exact=True,
): ):
self.permission_denied(request) self.permission_denied(request)
try:
self.add_user_to_voted_array(request.user, poll)
inform_changed_data(poll)
except IntegrityError:
raise ValidationError({"detail": "You have already voted"})
def parse_vote_value(self, obj, key): def parse_vote_value(self, obj, key):
""" Raises a ValidationError on incorrect values, including None """ """ Raises a ValidationError on incorrect values, including None """
@ -257,6 +265,13 @@ class BasePollViewSet(ModelViewSet):
""" """
pass pass
def add_user_to_voted_array(self, user, poll):
"""
To be implemented by subclass. Adds the given user to the voted array of the given poll.
Throws an IntegrityError if the user already exists in the array
"""
raise NotImplementedError()
def validate_vote_data(self, data, poll, user): def validate_vote_data(self, data, poll, user):
""" """
To be implemented by subclass. Validates the data according to poll type and method and fields by validated versions. To be implemented by subclass. Validates the data according to poll type and method and fields by validated versions.