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 %}