From ca8c0ce6146c5d2b815e0638cbf8290e48f86216 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Thu, 23 Nov 2017 21:07:16 +1100 Subject: [PATCH] Get started on voting booth --- .gitignore | 1 + build_js.sh | 3 + eos/base/election.py | 2 + eosweb/{ => core}/.bowerrc | 0 eosweb/core/bower.json | 3 +- eosweb/core/main.py | 15 +- eosweb/core/modelview.py | 3 + eosweb/core/static/js/booth_worker.js | 43 +++++ eosweb/core/static/nunjucks/booth/base.html | 53 ++++++ .../static/nunjucks/booth/selections.html | 58 +++++++ .../core/static/nunjucks/booth/welcome.html | 32 ++++ .../question/approval/selections_make.html | 68 ++++++++ eosweb/core/templates/election/booth.html | 162 +++++++++++++++++- 13 files changed, 436 insertions(+), 7 deletions(-) rename eosweb/{ => core}/.bowerrc (100%) create mode 100644 eosweb/core/static/js/booth_worker.js create mode 100644 eosweb/core/static/nunjucks/booth/base.html create mode 100644 eosweb/core/static/nunjucks/booth/selections.html create mode 100644 eosweb/core/static/nunjucks/booth/welcome.html create mode 100644 eosweb/core/static/nunjucks/question/approval/selections_make.html diff --git a/.gitignore b/.gitignore index 071e786..73bec35 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ __javascript__ __pycache__ refs bower_components +eosjs.js \#* .#* diff --git a/build_js.sh b/build_js.sh index b399ac5..fc765ab 100755 --- a/build_js.sh +++ b/build_js.sh @@ -39,3 +39,6 @@ for f in eos.js_tests; do # Transcrypt bug perl -0777 -pi -e 's/property.call \((.*?), \g1.\g1.__impl__(.*?)\)/property.call ($1, $1.__impl__$2)/g' eos/__javascript__/$f.js done + +cp eos/__javascript__/eos.js_tests.js eosweb/core/static/js/eosjs.js +perl -0777 -pi -e 's/eosjs_tests/eosjs/g' eosweb/core/static/js/eosjs.js diff --git a/eos/base/election.py b/eos/base/election.py index 57edd1d..b47702b 100644 --- a/eos/base/election.py +++ b/eos/base/election.py @@ -49,6 +49,8 @@ class Result(EmbeddedObject): class ApprovalQuestion(Question): choices = ListField(StringField()) + min_choices = IntField() + max_choices = IntField() class ApprovalAnswer(Answer): choices = ListField(IntField()) diff --git a/eosweb/.bowerrc b/eosweb/core/.bowerrc similarity index 100% rename from eosweb/.bowerrc rename to eosweb/core/.bowerrc diff --git a/eosweb/core/bower.json b/eosweb/core/bower.json index 35cdbfe..61a6d2a 100644 --- a/eosweb/core/bower.json +++ b/eosweb/core/bower.json @@ -16,6 +16,7 @@ "tests" ], "dependencies": { - "semantic": "semantic-ui#^2.2.13" + "semantic": "semantic-ui#^2.2.13", + "nunjucks": "^3.0.1" } } diff --git a/eosweb/core/main.py b/eosweb/core/main.py index d5ad746..df5d3d8 100644 --- a/eosweb/core/main.py +++ b/eosweb/core/main.py @@ -60,10 +60,10 @@ def setup_test_election(): election.sk = EGPrivateKey.generate() election.public_key = election.sk.public_key - question = ApprovalQuestion(prompt='President', choices=['John Smith', 'Joe Bloggs', 'John Q. Public']) + question = ApprovalQuestion(prompt='President', choices=['John Smith', 'Joe Bloggs', 'John Q. Public'], min_choices=0, max_choices=2) election.questions.append(question) - question = ApprovalQuestion(prompt='Chairman', choices=['John Doe', 'Andrew Citizen']) + question = ApprovalQuestion(prompt='Chairman', choices=['John Doe', 'Andrew Citizen'], min_choices=0, max_choices=1) election.questions.append(question) election.save() @@ -91,6 +91,11 @@ def using_election(func): return func(election) return wrapped +@app.route('/election//') +@using_election +def election_api_json(election): + return flask.Response(EosObject.to_json(EosObject.serialise_and_wrap(election, should_protect=True)), mimetype='application/json') + @app.route('/election//view') @using_election def election_view(election): @@ -99,7 +104,9 @@ def election_view(election): @app.route('/election//booth') @using_election def election_booth(election): - return flask.render_template('election/booth.html', election=election) + selection_model_view_map = EosObject.to_json({key._name: val for key, val in model_view_map.items()}) # ewww + + return flask.render_template('election/booth.html', election=election, selection_model_view_map=selection_model_view_map) @app.route('/election//view/questions') @using_election @@ -116,6 +123,8 @@ def election_view_ballots(election): def election_view_trustees(election): return flask.render_template('election/trustees.html', election=election) + + # === Model-Views === model_view_map = {} diff --git a/eosweb/core/modelview.py b/eosweb/core/modelview.py index 8dcd623..05d6637 100644 --- a/eosweb/core/modelview.py +++ b/eosweb/core/modelview.py @@ -26,5 +26,8 @@ model_view_map = { }, PSRElection: { 'tabs': 'election/psr/tabs.html' + }, + ApprovalQuestion: { + 'selections_make': 'question/approval/selections_make.html' } } diff --git a/eosweb/core/static/js/booth_worker.js b/eosweb/core/static/js/booth_worker.js new file mode 100644 index 0000000..12cc2a4 --- /dev/null +++ b/eosweb/core/static/js/booth_worker.js @@ -0,0 +1,43 @@ +/* + Eos - Verifiable elections + Copyright © 2017 RunasSudo (Yingtong Li) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +window = self; // Workaround for libraries +isLibrariesLoaded = false; + +function generateEncryptedVote(election, selections) { + encryptedVote = eos_js.eos_core.objects.__all__.PlaintextVote({ "choices": selections, "election_uuid": election.id, "election_hash": election.hash() }); + + postMessage(eos_js.eos_core.libobjects.__all__.EosObject.serialise_and_wrap(encryptedVote, null)); +} + +onmessage = function(msg) { + if (!isLibrariesLoaded) { + importScripts( + msg.data.static_base_url + "js/eosjs.js" + ); + isLibrariesLoaded = true; + } + + if (msg.data.action === "generateEncryptedVote") { + msg.data.election = eosjs.eos.core.libobjects.__all__.EosObject.deserialise_and_unwrap(msg.data.election, null); + + generateEncryptedVote(msg.data.election, msg.data.selections); + } else { + throw "Unknown action: " + msg.data.action; + } +} diff --git a/eosweb/core/static/nunjucks/booth/base.html b/eosweb/core/static/nunjucks/booth/base.html new file mode 100644 index 0000000..d2e997d --- /dev/null +++ b/eosweb/core/static/nunjucks/booth/base.html @@ -0,0 +1,53 @@ +{# + Eos - Verifiable elections + Copyright © 2017 RunasSudo (Yingtong Li) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +#} + +

{{ election.py_name }}

+ +

Election fingerprint: {{ eosjs.eos.core.hashing.__all__.SHA256().update_obj(election).hash_as_b64() }}

+ +{# Convert the template name to a numerical index for comparison #} +{% if template == 'booth/welcome.html' %} + {% set menuindex = 1 %} +{% elif template == 'booth/selections.html' %} + {% set menuindex = 2 %} +{% elif template == 'booth/review.html' %} + {% set menuindex = 3 %} +{% elif template == 'booth/audit.html' %} + {% set menuindex = 4 %} +{% elif template == 'booth/complete.html' %} + {% set menuindex = 5 %} +{% endif %} + + + +
+ {% block content %}{% endblock %} +
+ +
+ {% block buttons %}{% endblock %} +
+ +{% block after %}{% endblock %} diff --git a/eosweb/core/static/nunjucks/booth/selections.html b/eosweb/core/static/nunjucks/booth/selections.html new file mode 100644 index 0000000..bb7df1e --- /dev/null +++ b/eosweb/core/static/nunjucks/booth/selections.html @@ -0,0 +1,58 @@ +{% extends templates['booth/base.html'] %} + +{# + Eos - Verifiable elections + Copyright © 2017 RunasSudo (Yingtong Li) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +#} + +{% block content %} +
+{% endblock %} + +{% block buttons %} + +{% endblock %} + +{% block after %} + +{% endblock %} diff --git a/eosweb/core/static/nunjucks/booth/welcome.html b/eosweb/core/static/nunjucks/booth/welcome.html new file mode 100644 index 0000000..e3a24c5 --- /dev/null +++ b/eosweb/core/static/nunjucks/booth/welcome.html @@ -0,0 +1,32 @@ +{% extends templates['booth/base.html'] %} + +{# + Eos - Verifiable elections + Copyright © 2017 RunasSudo (Yingtong Li) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +#} + +{% block content %} +

Welcome to the {{ election.py_name }} voting booth.

+

Follow the on-screen directions to prepare and cast your ballot in this election. The bar above will show your progress. Please note that your ballot will not be cast until you complete the final ‘Cast ballot’ stage and receive a ‘smart ballot tracker’.

+

If at any point you wish to return to a previous screen, click the ‘Back’ button below.

+

If you wish, you may disconnect your internet connection now while preparing your ballot, however you must re-connect your internet connection before logging in to cast your ballot.

+

Please click the blue ‘Continue’ button below to continue.

+{% endblock %} + +{% block buttons %} + Back + +{% endblock %} diff --git a/eosweb/core/static/nunjucks/question/approval/selections_make.html b/eosweb/core/static/nunjucks/question/approval/selections_make.html new file mode 100644 index 0000000..432c74b --- /dev/null +++ b/eosweb/core/static/nunjucks/question/approval/selections_make.html @@ -0,0 +1,68 @@ +{# + Eos - Verifiable elections + Copyright © 2017 RunasSudo (Yingtong Li) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +#} + +

{{ questionNum + 1 }}. {{ election.questions[questionNum].prompt }}

+ +

Vote for between {{ election.questions[questionNum].min_choices }} and {{ election.questions[questionNum].max_choices }} candidates. Click the check-boxes to the left of the candidates' names to make your selection, then click the ‘Continue’ button. If you make a mistake, click the check-boxes again to clear your selection.

+ +
+
+ {% for choice in election.questions[questionNum].choices %} +
+
+ + +
+
+ {% endfor %} +
+
+ + + + diff --git a/eosweb/core/templates/election/booth.html b/eosweb/core/templates/election/booth.html index c7fc487..e3e83a2 100644 --- a/eosweb/core/templates/election/booth.html +++ b/eosweb/core/templates/election/booth.html @@ -21,7 +21,163 @@ {% block title %}{{ election.name }} – Voting booth{% endblock %} {% block content %} -

{{ election.name }}

- -

Election fingerprint: {{ SHA256().update_obj(election).hash_as_b64() }}

+
+
Loading voting booth. Please wait.
+
+{% endblock %} + +{% block basecontent %} + {{ super() }} + + + {% endblock %}