From 9fa74a2bef41c0c344f1c9a6f1c28a36f93347ea Mon Sep 17 00:00:00 2001 From: Ben Adida <ben@adida.net> Date: Mon, 29 May 2017 20:10:49 +0000 Subject: [PATCH] updated code to not use an unsaved User model instance within a password-only Voter, as that is considered unsafe in Django 1.8 --- helios/migrations/0004_auto_20170528_2025.py | 24 ++++++++++++++++++++ helios/models.py | 17 ++++++++------ helios/tasks.py | 6 ++--- helios/tests.py | 15 ++++-------- helios/views.py | 7 +++--- requirements.txt | 2 +- 6 files changed, 47 insertions(+), 24 deletions(-) create mode 100644 helios/migrations/0004_auto_20170528_2025.py diff --git a/helios/migrations/0004_auto_20170528_2025.py b/helios/migrations/0004_auto_20170528_2025.py new file mode 100644 index 0000000..d49bb63 --- /dev/null +++ b/helios/migrations/0004_auto_20170528_2025.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('helios', '0003_auto_20160507_1948'), + ] + + operations = [ + migrations.AlterField( + model_name='election', + name='help_email', + field=models.EmailField(max_length=254, null=True), + ), + migrations.AlterField( + model_name='trustee', + name='email', + field=models.EmailField(max_length=254), + ), + ] diff --git a/helios/models.py b/helios/models.py index 86a31a5..96b6654 100644 --- a/helios/models.py +++ b/helios/models.py @@ -817,9 +817,9 @@ class Voter(HeliosModel): def __init__(self, *args, **kwargs): super(Voter, self).__init__(*args, **kwargs) + def get_user(self): # stub the user so code is not full of IF statements - if not self.user: - self.user = User(user_type='password', user_id=self.voter_email, name=self.voter_name) + return self.user or User(user_type='password', user_id=self.voter_email, name=self.voter_name) @classmethod @transaction.atomic @@ -920,11 +920,11 @@ class Voter(HeliosModel): @property def name(self): - return self.user.name + return self.get_user().name @property def voter_id(self): - return self.user.user_id + return self.get_user().user_id @property def voter_id_hash(self): @@ -945,14 +945,17 @@ class Voter(HeliosModel): @property def voter_type(self): - return self.user.user_type + return self.get_user().user_type @property def display_html_big(self): - return self.user.display_html_big + return self.get_user().display_html_big def send_message(self, subject, body): - self.user.send_message(subject, body) + self.get_user().send_message(subject, body) + + def can_update_status(self): + return self.get_user().can_update_status() def generate_password(self, length=10): if self.voter_password: diff --git a/helios/tasks.py b/helios/tasks.py index 7b30fbb..5f7bba8 100644 --- a/helios/tasks.py +++ b/helios/tasks.py @@ -22,7 +22,7 @@ def cast_vote_verify_and_store(cast_vote_id, status_update_message=None, **kwarg voter = cast_vote.voter election = voter.election - user = voter.user + user = voter.get_user() if result: # send the signal @@ -71,7 +71,7 @@ def single_voter_email(voter_uuid, subject_template, body_template, extra_vars={ subject = render_template_raw(None, subject_template, the_vars) body = render_template_raw(None, body_template, the_vars) - voter.user.send_message(subject, body) + voter.send_message(subject, body) @task() def single_voter_notify(voter_uuid, notification_template, extra_vars={}): @@ -82,7 +82,7 @@ def single_voter_notify(voter_uuid, notification_template, extra_vars={}): notification = render_template_raw(None, notification_template, the_vars) - voter.user.send_notification(notification) + voter.send_notification(notification) @task() def election_compute_tally(election_id): diff --git a/helios/tests.py b/helios/tests.py index 13557ae..542df03 100644 --- a/helios/tests.py +++ b/helios/tests.py @@ -250,7 +250,7 @@ class VoterModelTests(TestCase): self.assertRaises(Exception, lambda: v.generate_password()) # check that you can get at the voter user structure - self.assertEquals(v.user.user_id, v.voter_email) + self.assertEquals(v.get_user().user_id, v.voter_email) class CastVoteModelTests(TestCase): @@ -424,8 +424,8 @@ class ElectionBlackboxTests(WebTest): # set up the app, too # this does not appear to work, boohoo - session = self.app.session - session['user'] = {'type': self.user.user_type, 'user_id': self.user.user_id} + #session = self.app.session + #session['user'] = {'type': self.user.user_type, 'user_id': self.user.user_id} def clear_login(self): session = self.client.session @@ -642,7 +642,7 @@ class ElectionBlackboxTests(WebTest): self.assertRedirects(response, "%s/helios/elections/%s/cast_confirm" % (settings.SECURE_URL_HOST, election_id)) cast_confirm_page = response.follow() - + if need_login: if check_user_logged_in: self.assertContains(cast_confirm_page, "You are logged in as") @@ -653,12 +653,7 @@ class ElectionBlackboxTests(WebTest): login_form['voter_id'] = username login_form['password'] = password - # we skip that intermediary page now - # cast_confirm_page = login_form.submit() response = login_form.submit() - - # self.assertRedirects(cast_confirm_page, "/helios/elections/%s/cast_confirm" % election_id) - # cast_confirm_page = cast_confirm_page.follow() else: # here we should be at the cast-confirm page and logged in self.assertContains(cast_confirm_page, "CAST this ballot") @@ -751,7 +746,7 @@ class ElectionBlackboxTests(WebTest): ## for now the above does not work, it's a testing problem ## where the cookie isn't properly set. We'll have to figure this out. ## FIXME FIXME FIXME - # self._cast_ballot(election_id, username, password, check_user_logged_in=True) + #self._cast_ballot(election_id, username, password, check_user_logged_in=True) self._cast_ballot(election_id, username, password, check_user_logged_in=False) self.clear_login() diff --git a/helios/views.py b/helios/views.py index d51191b..2959463 100644 --- a/helios/views.py +++ b/helios/views.py @@ -622,7 +622,7 @@ def one_election_cast_confirm(request, election): return render_template(request, 'election_not_started', {'election': election}) voter = get_voter(request, user, election) - + # auto-register this person if the election is openreg if user and not voter and election.openreg: voter = _register_voter(election, user) @@ -677,7 +677,7 @@ def one_election_cast_confirm(request, election): bad_voter_login = (request.GET.get('bad_voter_login', "0") == "1") # status update this vote - if voter and voter.user.can_update_status(): + if voter and voter.can_update_status(): status_update_label = voter.user.update_status_template() % "your smart ballot tracker" status_update_message = "I voted in %s - my smart tracker is %s.. #heliosvoting" % (get_election_url(election),cast_vote.vote_hash[:10]) else: @@ -761,7 +761,8 @@ def one_election_cast_done(request, election): # only log out if the setting says so *and* we're dealing # with a site-wide voter. Definitely remove current_voter - if voter.user == user: + # checking that voter.user != None is needed because voter.user may now be None if voter is password only + if voter.user == user and voter.user != None: logout = settings.LOGOUT_ON_CONFIRMATION else: logout = False diff --git a/requirements.txt b/requirements.txt index 6bf946f..4f2b599 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Django==1.7.10 +Django==1.8.18 anyjson==0.3.3 celery==3.1.18 django-celery==3.1.16 -- GitLab