diff --git a/eos/base/election.py b/eos/base/election.py index d87aa63..321f823 100644 --- a/eos/base/election.py +++ b/eos/base/election.py @@ -47,8 +47,13 @@ class Ballot(EmbeddedObject): return Ballot(encrypted_answers=encrypted_answers_deaudit, election_id=self.election_id, election_hash=self.election_hash) class Vote(EmbeddedObject): + _ver = StringField(default='0.4') + ballot = EmbeddedObjectField() cast_at = DateTimeField() + + cast_ip = StringField(is_protected=True) + cast_fingerprint = BlobField(is_protected=True) class Voter(EmbeddedObject): _id = UUIDField() diff --git a/eos/core/objects/__init__.py b/eos/core/objects/__init__.py index 994105b..3473170 100644 --- a/eos/core/objects/__init__.py +++ b/eos/core/objects/__init__.py @@ -84,6 +84,7 @@ DictField = PrimitiveField IntField = PrimitiveField StringField = PrimitiveField BooleanField = PrimitiveField +BlobField = PrimitiveField class EmbeddedObjectField(Field): def __init__(self, object_type=None, *args, **kwargs): diff --git a/eosweb/core/bower.json b/eosweb/core/bower.json index 3587672..0c86f5f 100644 --- a/eosweb/core/bower.json +++ b/eosweb/core/bower.json @@ -18,6 +18,7 @@ "dependencies": { "semantic": "semantic-ui#^2.2.13", "nunjucks": "^3.0.1", - "dragula.js": "dragula#^3.7.2" + "dragula.js": "dragula#^3.7.2", + "fingerprintjs2": "^1.5.1" } } diff --git a/eosweb/core/main.py b/eosweb/core/main.py index 339e6f4..85685ab 100644 --- a/eosweb/core/main.py +++ b/eosweb/core/main.py @@ -252,6 +252,16 @@ def election_api_cast_vote(election): # Cast the vote ballot = EosObject.deserialise_and_unwrap(data['ballot']) vote = Vote(ballot=ballot, cast_at=DateTimeField.now()) + + # Store data + if app.config['CAST_FINGERPRINT']: + vote.cast_fingerprint = data['fingerprint'] + if app.config['CAST_IP']: + if os.path.exists('/app/.heroku'): + vote.cast_ip = flask.request.headers['X-Forwarded-For'].split(',')[-1] + else: + vote.cast_ip = flask.request.remote_addr + voter.votes.append(vote) election.save() diff --git a/eosweb/core/static/js/booth_worker.js b/eosweb/core/static/js/booth_worker.js index acfa65d..d1fc56e 100644 --- a/eosweb/core/static/js/booth_worker.js +++ b/eosweb/core/static/js/booth_worker.js @@ -19,7 +19,7 @@ window = self; // Workaround for libraries isLibrariesLoaded = false; -function generateEncryptedVote(election, answers) { +function generateEncryptedVote(election, answers, should_do_fingerprint) { encrypted_answers = []; for (var q_num = 0; q_num < answers.length; q_num++) { answer_json = answers[q_num]; @@ -28,7 +28,9 @@ function generateEncryptedVote(election, answers) { encrypted_answers.push(eosjs.eos.core.objects.__all__.EosObject.serialise_and_wrap(encrypted_answer, null)); } - postMessage(encrypted_answers); + postMessage({ + encrypted_answers: encrypted_answers + }); } onmessage = function(msg) { diff --git a/eosweb/core/static/nunjucks/booth/cast.html b/eosweb/core/static/nunjucks/booth/cast.html index ceb54d2..e75513e 100644 --- a/eosweb/core/static/nunjucks/booth/cast.html +++ b/eosweb/core/static/nunjucks/booth/cast.html @@ -90,7 +90,8 @@ url: "{{ election_base_url }}cast_ballot", type: "POST", data: eosjs.eos.core.objects.__all__.EosObject.to_json({ - "ballot": eosjs.eos.core.objects.__all__.EosObject.serialise_and_wrap(booth.ballot, null) + "ballot": eosjs.eos.core.objects.__all__.EosObject.serialise_and_wrap(booth.ballot, null), + "fingerprint": booth.fingerprint || null }), contentType: "application/json", dataType: "text" diff --git a/eosweb/core/static/nunjucks/booth/encrypt.html b/eosweb/core/static/nunjucks/booth/encrypt.html index 74412ea..6e18105 100644 --- a/eosweb/core/static/nunjucks/booth/encrypt.html +++ b/eosweb/core/static/nunjucks/booth/encrypt.html @@ -27,7 +27,7 @@ } encryptedAnswers = []; - for (var encrypted_answer_json of msg.data) { + for (var encrypted_answer_json of msg.data.encrypted_answers) { encryptedAnswers.push(eosjs.eos.core.objects.__all__.EosObject.deserialise_and_unwrap(encrypted_answer_json, null)); } @@ -37,7 +37,18 @@ booth.ballot.election_id = election._id; booth.ballot.election_hash = eosjs.eos.core.hashing.__all__.SHA256().update_obj(election).hash_as_b64(); - nextTemplate(); + if (should_do_fingerprint) { + // String.prototype.join confuses fingerprintjs2 + var strjoin = String.prototype.join; + String.prototype.join = undefined; + new Fingerprint2().get(function(result, components) { + String.prototype.join = strjoin; + booth.fingerprint = components; + nextTemplate(); + }); + } else { + nextTemplate(); + } } catch (err) { boothError(err); throw err; @@ -52,7 +63,7 @@ "action": "generateEncryptedVote", "static_base_url": "{{ static_base_url }}", "election": eosjs.eos.core.objects.__all__.EosObject.serialise_and_wrap(election, null), - "answers": booth.answers, + "answers": booth.answers }); } catch (err) { boothError(err); diff --git a/eosweb/core/templates/election/view/booth.html b/eosweb/core/templates/election/view/booth.html index a294d62..6fa0436 100644 --- a/eosweb/core/templates/election/view/booth.html +++ b/eosweb/core/templates/election/view/booth.html @@ -38,6 +38,9 @@ + {% if eosweb.app.config['CAST_FINGERPRINT'] %} + + {% endif %}