From 78dc2c7ee1d8026adfed1e89cf9fdffe97e1fe1b Mon Sep 17 00:00:00 2001 From: Ben Adida <ben@adida.net> Date: Thu, 15 May 2014 20:56:30 -0700 Subject: [PATCH] added step for release result --- helios/election_urls.py | 1 + helios/models.py | 9 ++++++ helios/templates/combine_decryptions.html | 8 +++--- helios/templates/election_view.html | 34 ++++++++++++++--------- helios/templates/release_result.html | 16 +++++++++++ helios/tests.py | 14 ++++++++-- helios/views.py | 31 +++++++++++++++++++-- 7 files changed, 91 insertions(+), 22 deletions(-) create mode 100644 helios/templates/release_result.html diff --git a/helios/election_urls.py b/helios/election_urls.py index 459e131..9b0f874 100644 --- a/helios/election_urls.py +++ b/helios/election_urls.py @@ -63,6 +63,7 @@ urlpatterns = patterns('', # computing tally (r'^/compute_tally$', one_election_compute_tally), (r'^/combine_decryptions$', combine_decryptions), + (r'^/release_result$', release_result), # casting a ballot before we know who the voter is (r'^/cast$', one_election_cast), diff --git a/helios/models.py b/helios/models.py index e338bbd..d097b49 100644 --- a/helios/models.py +++ b/helios/models.py @@ -391,6 +391,15 @@ class Election(HeliosModel): return True + def release_result(self): + """ + release the result that should already be computed + """ + if not self.result: + return + + self.result_released_at = datetime.datetime.utcnow() + def combine_decryptions(self): """ combine all of the decryption results diff --git a/helios/templates/combine_decryptions.html b/helios/templates/combine_decryptions.html index e83e1f8..f5c9bea 100644 --- a/helios/templates/combine_decryptions.html +++ b/helios/templates/combine_decryptions.html @@ -1,16 +1,16 @@ {% extends TEMPLATE_BASE %} -{% block title %}Release Tally — {{election.name}}{% endblock %} +{% block title %}Compute Tally — {{election.name}}{% endblock %} {% block content %} - <h2 class="title">{{election.name}} — Release Tally <span style="font-size:0.7em;">[<a href="{% url helios.views.one_election_view election.uuid %}">cancel</a>]</span></h2> + <h2 class="title">{{election.name}} — Compute Tally <span style="font-size:0.7em;">[<a href="{% url helios.views.one_election_view election.uuid %}">cancel</a>]</span></h2> <p> - You are about to release the tally for this election + You are about to compute the tally for this election. You only will then see the results. </p> <form method="POST" action=""> <input type="hidden" name="csrf_token" value="{{csrf_token}}" /> - <input type="submit" value="release the tally!" /> + <input type="submit" value="compute the tally!" /> </form> {% endblock %} diff --git a/helios/templates/election_view.html b/helios/templates/election_view.html index 5cbe997..3bd61e2 100644 --- a/helios/templates/election_view.html +++ b/helios/templates/election_view.html @@ -77,7 +77,7 @@ this {{election.election_type}} is <u>not</u> featured on the front page. {% endif %} <p> -{% if election.result %} +{% if election.result_released_at %} <!-- election complete, no next step --> @@ -113,20 +113,25 @@ trustees will be asked to provide their share of the decryption. {% endif %} {% else %} +{% if election.result %} +<a href="{% url helios.views.release_result election.uuid %}">release result</a><br /> +The result displayed below is visible only to you.<br /> +Once you release the result, it will be visible to everyone. +{% else %} + {% if election.ready_for_decryption_combination %} <a href="{% url helios.views.combine_decryptions election.uuid %}"> {% if election.num_trustees == 1 %} -release results +compute results {% else %} -combine trustee decryptions and release results +combine trustee decryptions and compute results {% endif %} </a><br /> {% if election.num_trustees == 1 %} -The result is released and all voters are notified. +The result will be computed and shown to you, the administrator, only. {% else %} -The decryption shares from the trustees are combined and the tally is decrypted.<br /> -Once you do this, the tally will be immediately available for all to see, and -all voters will be notified that the tally is ready. +The decryption shares from the trustees will be combined and the tally computed.<br /> +Once you do this, the tally will visible to you, the administrator, only. {% endif %} {% else %} <a href="{% url helios.views.list_trustees_view election.uuid %}">trustees (for decryption)</a> @@ -134,6 +139,8 @@ all voters will be notified that the tally is ready. {% endif %} +{% endif %} + {% endif %} </span> @@ -143,14 +150,15 @@ all voters will be notified that the tally is ready. {% endif %} -<br /><br /> +<br /> -{% if election.result %} +{% if show_result %} +{% if election.result_released_at %} <span class="highlight-box round"> This election is complete. -</span><br /> +</span><br /><br /><br /> +{% endif %} -<br /> <h3 class="highlight-box">Tally</h3> {% for question in election.pretty_result %} <b><span style="font-size:0.8em;">Question #{{forloop.counter}}</span><br />{{question.question}}</b><br /> @@ -165,8 +173,8 @@ all voters will be notified that the tally is ready. {% if election.voting_has_stopped %} <span class="highlight-box round"> - Election closed. Tally will be computed soon. -</span><br /> + Election closed. Results will be released soon. +</span><br /><br /> {% else %} {% if election.voting_has_started %} diff --git a/helios/templates/release_result.html b/helios/templates/release_result.html new file mode 100644 index 0000000..5dc4276 --- /dev/null +++ b/helios/templates/release_result.html @@ -0,0 +1,16 @@ +{% extends TEMPLATE_BASE %} + +{% block title %}Release Result — {{election.name}}{% endblock %} +{% block content %} + <h2 class="title">{{election.name}} — Release Result <span style="font-size:0.7em;">[<a href="{% url helios.views.one_election_view election.uuid %}">cancel</a>]</span></h2> + + <p> + You are about to release the result for this election. + </p> + + <form method="POST" action=""> + <input type="hidden" name="csrf_token" value="{{csrf_token}}" /> + <input type="submit" value="release result!" /> + </form> + +{% endblock %} diff --git a/helios/tests.py b/helios/tests.py index eeb9cac..b39c3a8 100644 --- a/helios/tests.py +++ b/helios/tests.py @@ -721,9 +721,17 @@ class ElectionBlackboxTests(WebTest): "csrf_token" : self.client.session['csrf_token'], }) - # after tallying, we now are supposed to see the email screen - # with the right template value of 'result' - self.assertRedirects(response, "/helios/elections/%s/voters/email?template=result" % election_id) + # after tallying, we now go back to election_view + self.assertRedirects(response, "/helios/elections/%s/view" % election_id) + + # check that we can't get the tally yet + response = self.client.get("/helios/elections/%s/result" % election_id) + self.assertEquals(response.status_code, 403) + + # release + response = self.client.post("/helios/elections/%s/release_result" % election_id, { + "csrf_token" : self.client.session['csrf_token'], + }) # check that tally matches response = self.client.get("/helios/elections/%s/result" % election_id) diff --git a/helios/views.py b/helios/views.py index 65d8895..f6ed5cb 100644 --- a/helios/views.py +++ b/helios/views.py @@ -8,6 +8,7 @@ Ben Adida (ben@adida.net) from django.core.urlresolvers import reverse from django.core.mail import send_mail from django.core.paginator import Paginator +from django.core.exceptions import PermissionDenied from django.http import * from django.db import transaction @@ -333,11 +334,15 @@ def one_election_view(request, election): trustees = Trustee.get_by_election(election) + # should we show the result? + show_result = election.result_released_at or (election.result and admin_p) + return render_template(request, 'election_view', {'election' : election, 'trustees': trustees, 'admin_p': admin_p, 'user': user, 'voter': voter, 'votes': votes, 'notregistered': notregistered, 'eligible_p': eligible_p, 'can_feature_p': can_feature_p, 'election_url' : election_url, 'vote_url': vote_url, 'election_badge_url' : election_badge_url, + 'show_result': show_result, 'test_cookie_url': test_cookie_url, 'socialbuttons_url' : socialbuttons_url}) def test_cookie(request): @@ -774,11 +779,15 @@ def one_election_cast_done(request, election): @election_view() @json def one_election_result(request, election): + if not election.result_released_at: + raise PermissionDenied return election.result @election_view() @json def one_election_result_proof(request, election): + if not election.result_released_at: + raise PermissionDenied return election.result_proof @election_view(frozen=True) @@ -1040,7 +1049,25 @@ def trustee_upload_decryption(request, election, trustee_uuid): return SUCCESS else: return FAILURE - + +@election_admin(frozen=True) +def release_result(request, election): + """ + result is computed and now it's time to release the result + """ + election_url = get_election_url(election) + + if request.method == "POST": + check_csrf(request) + + election.release_result() + election.save() + + return HttpResponseRedirect("%s" % (settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))) + + # if just viewing the form or the form is not valid + return render_template(request, 'release_result', {'election': election}) + @election_admin(frozen=True) def combine_decryptions(request, election): """ @@ -1055,7 +1082,7 @@ def combine_decryptions(request, election): election.combine_decryptions() election.save() - return HttpResponseRedirect("%s?%s" % (settings.SECURE_URL_HOST + reverse(voters_email, args=[election.uuid]), urllib.urlencode({'template': 'result'}))) + return HttpResponseRedirect("%s" % (settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))) # if just viewing the form or the form is not valid return render_template(request, 'combine_decryptions', {'election': election}) -- GitLab