Make sure users only vote once in the same poll

When skipping verification, we cannot apply the validation rule saying
the document number and document type must be unique, because they'll be
`nil` in many cases. So we were skipping the rule, but that makes it
possible for the same user to vote several times (for instance, once in
a booth and once via web).

So we're changing the scope of the uniqueness rule: instead of being
unique per document number, voters are unique per user. The reason we
made them unique per document number was that back in commit 900563e3
(when we added the rule), we hadn't added the relation between users and
poll voters yet.
This commit is contained in:
Javi Martín
2020-08-06 22:05:39 +02:00
parent 4367b2054a
commit 096f546c24
4 changed files with 32 additions and 5 deletions

View File

@@ -14,7 +14,8 @@ class Poll
validates :booth_assignment_id, presence: true, if: ->(voter) { voter.origin == "booth" } validates :booth_assignment_id, presence: true, if: ->(voter) { voter.origin == "booth" }
validates :officer_assignment_id, presence: true, if: ->(voter) { voter.origin == "booth" } validates :officer_assignment_id, presence: true, if: ->(voter) { voter.origin == "booth" }
validates :document_number, presence: true, uniqueness: { scope: [:poll_id, :document_type], message: :has_voted }, unless: :skip_user_verification? validates :document_number, presence: true, unless: :skip_user_verification?
validates :user_id, uniqueness: { scope: [:poll_id], message: :has_voted }
validates :origin, inclusion: { in: VALID_ORIGINS } validates :origin, inclusion: { in: VALID_ORIGINS }
before_validation :set_demographic_info, :set_document_info, :set_denormalized_booth_assignment_id before_validation :set_demographic_info, :set_document_info, :set_denormalized_booth_assignment_id

View File

@@ -508,6 +508,7 @@ en:
attributes: attributes:
document_number: document_number:
not_in_census: "Document not in census" not_in_census: "Document not in census"
user_id:
has_voted: "User has already voted" has_voted: "User has already voted"
legislation/process: legislation/process:
attributes: attributes:

View File

@@ -510,6 +510,7 @@ es:
attributes: attributes:
document_number: document_number:
not_in_census: "Este documento no aparece en el censo" not_in_census: "Este documento no aparece en el censo"
user_id:
has_voted: "Este usuario ya ha votado" has_voted: "Este usuario ya ha votado"
legislation/process: legislation/process:
attributes: attributes:

View File

@@ -34,7 +34,7 @@ describe Poll::Voter do
voter = build(:poll_voter, user: user, poll: poll) voter = build(:poll_voter, user: user, poll: poll)
expect(voter).not_to be_valid expect(voter).not_to be_valid
expect(voter.errors.messages[:document_number]).to eq(["User has already voted"]) expect(voter.errors.messages[:user_id]).to eq(["User has already voted"])
end end
it "is not valid if the user has already voted in the same poll/booth" do it "is not valid if the user has already voted in the same poll/booth" do
@@ -43,7 +43,7 @@ describe Poll::Voter do
voter = build(:poll_voter, user: user, poll: poll, booth_assignment: booth_assignment) voter = build(:poll_voter, user: user, poll: poll, booth_assignment: booth_assignment)
expect(voter).not_to be_valid expect(voter).not_to be_valid
expect(voter.errors.messages[:document_number]).to eq(["User has already voted"]) expect(voter.errors.messages[:user_id]).to eq(["User has already voted"])
end end
it "is not valid if the user has already voted in different booth in the same poll" do it "is not valid if the user has already voted in different booth in the same poll" do
@@ -52,7 +52,7 @@ describe Poll::Voter do
voter = build(:poll_voter, :from_booth, user: user, poll: poll, booth: booth) voter = build(:poll_voter, :from_booth, user: user, poll: poll, booth: booth)
expect(voter).not_to be_valid expect(voter).not_to be_valid
expect(voter.errors.messages[:document_number]).to eq(["User has already voted"]) expect(voter.errors.messages[:user_id]).to eq(["User has already voted"])
end end
it "is valid if the user has already voted in the same booth in different poll" do it "is valid if the user has already voted in the same booth in different poll" do
@@ -69,7 +69,31 @@ describe Poll::Voter do
voter = build(:poll_voter, poll: answer.question.poll, user: answer.author) voter = build(:poll_voter, poll: answer.question.poll, user: answer.author)
expect(voter).not_to be_valid expect(voter).not_to be_valid
expect(voter.errors.messages[:document_number]).to eq(["User has already voted"]) expect(voter.errors.messages[:user_id]).to eq(["User has already voted"])
end
context "Skip verification is enabled" do
before do
Setting["feature.user.skip_verification"] = true
user.update!(document_number: nil, document_type: nil)
end
it "is not valid if the user has already voted in the same poll" do
create(:poll_voter, user: user, poll: poll)
voter = build(:poll_voter, user: user, poll: poll)
expect(voter).not_to be_valid
end
it "is valid if other users have voted in the same poll" do
another_user = create(:user, :level_two, document_number: nil, document_type: nil)
create(:poll_voter, user: another_user, poll: poll)
voter = build(:poll_voter, user: user, poll: poll)
expect(voter).to be_valid
end
end end
context "origin" do context "origin" do