Updated various parts of the application to improve usability. Started laying some of the ground work for a bulletin board. Added informative emails that keep voters, trustees and organisers updated with the state of an event.
This commit is contained in:
parent
59d3b26e95
commit
25a7f72160
7 changed files with 138 additions and 57 deletions
|
@ -206,6 +206,7 @@ class Ballot(models.Model):
|
|||
voter = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="ballots")
|
||||
poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="ballots")
|
||||
selection = models.CharField(max_length=1)
|
||||
json_str = models.CharField(max_length=10240)
|
||||
cast = models.BooleanField(default=False)
|
||||
|
||||
|
||||
|
|
|
@ -68,6 +68,26 @@ def get_email_sign_off():
|
|||
return sign_off
|
||||
|
||||
|
||||
def email_e_results_ready(event):
|
||||
event_title = event.title
|
||||
email_subject = "Event Results for '" + event_title + "' Have Been Decrypted"
|
||||
|
||||
# Construct the email body. This can be later replaced by a HTML email.
|
||||
email_body = str("")
|
||||
email_body += "Dear Event Organiser,\n\n"
|
||||
email_body += "This email is to inform you that all of the partial decryptions for the event '" + event_title + \
|
||||
"' have been supplied. These have been used to decrypt the results which are now available.\n\n"
|
||||
email_body += "As an organiser, the event results can be found at the following URL:\n\n"
|
||||
email_body += "http://" + settings.DOMAIN + "/event/" + str(event.pk) + "/results/"
|
||||
email_body += get_email_sign_off()
|
||||
|
||||
# Get all of the organisers for the event and send them an email
|
||||
organisers = event.users_organisers.all()
|
||||
|
||||
for organiser in organisers:
|
||||
organiser.email_user(email_subject, email_body)
|
||||
|
||||
|
||||
'''
|
||||
Combines all of the voter ballots for a poll option into a single 'CombinedBallot'
|
||||
'''
|
||||
|
@ -99,6 +119,11 @@ def combine_ballots(polls):
|
|||
|
||||
combined_cipher = add_ciphers(ciphers)
|
||||
|
||||
# If a combined ballot already exists, clear it
|
||||
if CombinedBallot.objects.filter(poll=poll, option=option).exists():
|
||||
CombinedBallot.objects.filter(poll=poll, option=option).delete()
|
||||
|
||||
# Create a combined ballot for this option in the poll
|
||||
CombinedBallot.objects.create(poll=poll,
|
||||
option=option,
|
||||
cipher_text_c1=combined_cipher['C1'],
|
||||
|
@ -179,6 +204,30 @@ def email_trustees_prep(trustees, event):
|
|||
trustee.send_email(email_subject, email_body)
|
||||
|
||||
|
||||
'''
|
||||
Task triggered when all trustees have supplied their partial public keys and the event has been prepared
|
||||
'''
|
||||
@task()
|
||||
def email_organisers_next_steps(event):
|
||||
event_title = event.title
|
||||
email_subject = "Event '" + event_title + "' Successfully Prepared by All Trustees"
|
||||
|
||||
email_body = str("")
|
||||
email_body += "Dear Event Organiser,\n\n"
|
||||
email_body += "This email is to inform you that all trustees have supplied their public keys for the event '" \
|
||||
+ event_title + "'. The event is therefore prepared and ready to accept votes when voting opens.\n\n"
|
||||
email_body += "Once voting has ended, you need to visit the following URL to begin the decryption process:\n\n"
|
||||
email_body += "http://" + settings.DOMAIN + "/event/" + str(event.pk) + "/\n\n"
|
||||
email_body += "Once you've accessed the page, simply hit the 'End' button to email all trustees to ask for their " \
|
||||
"partial decryptions for all polls for this event."
|
||||
email_body += get_email_sign_off()
|
||||
|
||||
# Get the list of organisers that need emailing and email them
|
||||
organisers = event.users_organisers.all()
|
||||
|
||||
for organiser in organisers:
|
||||
organiser.email_user(email_subject, email_body)
|
||||
|
||||
'''
|
||||
Emails a URL containing an access key for all of the voters for an event
|
||||
'''
|
||||
|
@ -317,3 +366,6 @@ def combine_decryptions_and_tally(event):
|
|||
poll.result_json = result
|
||||
poll.save()
|
||||
|
||||
# Email the list of organisers to inform them that the results for this event are ready
|
||||
email_e_results_ready(event)
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ from allauthdemo.auth.models import DemoUser
|
|||
|
||||
from .tasks import email_trustees_prep, update_EID, generate_combpk, event_ended, create_ballots
|
||||
from .tasks import create_ballots_for_poll, email_voters_vote_url, combine_decryptions_and_tally, combine_encrypted_votes
|
||||
from .tasks import email_voting_success
|
||||
from .tasks import email_voting_success, email_organisers_next_steps
|
||||
|
||||
from .utils.EventModelAdaptor import EventModelAdaptor
|
||||
|
||||
|
@ -187,7 +187,8 @@ def event_vote(request, event_id, poll_id):
|
|||
cant_vote_reason = "The event either isn't ready for voting or it has expired and therefore you cannot vote."
|
||||
|
||||
if request.method == "POST":
|
||||
ballot_json = json.loads(request.POST.get('ballot'))
|
||||
ballot_str = request.POST.get('ballot')
|
||||
ballot_json = json.loads(ballot_str)
|
||||
selection = request.POST.get('selection')
|
||||
encrypted_votes_json = ballot_json['encryptedVotes']
|
||||
|
||||
|
@ -219,6 +220,7 @@ def event_vote(request, event_id, poll_id):
|
|||
|
||||
ballot.cast = True
|
||||
ballot.selection = selection
|
||||
ballot.json_str = ballot_str
|
||||
ballot.save()
|
||||
|
||||
voter = email_key[0].user
|
||||
|
@ -270,6 +272,7 @@ def event_trustee_setup(request, event_id):
|
|||
create_ballots.delay(event)
|
||||
generate_combpk.delay(event)
|
||||
email_voters_vote_url.delay(event.voters.all(), event)
|
||||
email_organisers_next_steps.delay(event)
|
||||
|
||||
success_msg = 'You have successfully submitted your public key for this event!'
|
||||
messages.add_message(request, messages.SUCCESS, success_msg)
|
||||
|
@ -365,8 +368,14 @@ def event_trustee_decrypt(request, event_id):
|
|||
text=part_dec)
|
||||
|
||||
if event.all_part_decs_received():
|
||||
# TODO: Combine partial decryptions and gen results
|
||||
# Decrypt the result once all partial decryptions have been received
|
||||
# This will email all organisers once the results are ready
|
||||
combine_decryptions_and_tally.delay(event)
|
||||
else:
|
||||
# TODO: Get how many trustees have submitted a partial decryption
|
||||
# TODO: Then get how many are left to submit their partial decryptions
|
||||
# TODO: Then email the list of organisers to update them with this information
|
||||
str("")
|
||||
|
||||
messages.add_message(request, messages.SUCCESS, 'Your partial decryptions have been successfully submitted')
|
||||
return HttpResponseRedirect(reverse("user_home"))
|
||||
|
|
|
@ -76,39 +76,7 @@
|
|||
|
||||
//new function
|
||||
demosEncrypt.decryptSubmitCiphers = function() {
|
||||
var skString = $('#secret-key').val();
|
||||
if (!skString) {
|
||||
alert("Please enter your secret key");
|
||||
}
|
||||
else {
|
||||
//rebuild our secret key
|
||||
var ctx = new CTX("BN254CX");
|
||||
var skBytes = skString.split(",");
|
||||
var sk = new ctx.BIG.fromBytes(skBytes);
|
||||
|
||||
var inputs = $("form input[type=text]");
|
||||
|
||||
inputs.each(function() { //for each ciphertext to decrypt
|
||||
var ciphertext = {
|
||||
C1: null,
|
||||
C2: null
|
||||
};
|
||||
|
||||
var temp = JSON.parse($(this).val());
|
||||
var c1Bytes = getBytes(temp.C1.split(','));
|
||||
ciphertext.C1 = new ctx.ECP.fromBytes(c1Bytes);
|
||||
|
||||
var c2Bytes = getBytes(temp.C2.split(','));
|
||||
ciphertext.C2 = new ctx.ECP.fromBytes(c2Bytes);
|
||||
|
||||
// Perform partial decryption where the method returns an object containing an ECP()
|
||||
var partial = partDec(sk, ciphertext);
|
||||
|
||||
var bytes = [];
|
||||
partial.D.toBytes(bytes);
|
||||
$(this).val(bytes.toString());//submit in byte array form
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//new function
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<h2>Trustee Event Decryption for Event '{{ event.title }}'</h2>
|
||||
<hr/>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Submit your Secret Key as '{{ user_email }}'</strong></div>
|
||||
<div class="panel-heading"><strong>Upload your Secret Key as '{{ user_email }}'</strong></div>
|
||||
<div class="panel panel-body">
|
||||
<input id="secret-key" name="secret-key" class="textinput textInput form-control" type="text" disabled/>
|
||||
<div class="alert alert-info" role="alert" style="margin-top: 0.75em;">
|
||||
|
@ -30,7 +30,7 @@
|
|||
<input type="file" id="files_sk_upload" name="file" class="btn-info">
|
||||
</div>
|
||||
<br/>
|
||||
<div class="panel-heading"><strong>Ciphers</strong></div>
|
||||
<div class="panel-heading"><strong>Encrypted Event Data</strong></div>
|
||||
<div class="panel panel-body">
|
||||
<form id="cipher-form" method="POST">
|
||||
{% csrf_token %}
|
||||
|
@ -48,25 +48,26 @@
|
|||
<br/>
|
||||
{% endfor %}
|
||||
<button id="decrypt-btn"
|
||||
onclick="demosEncrypt.decryptSubmitCiphers()"
|
||||
|
||||
onclick="decryptSubmitCiphers()"
|
||||
class="btn btn-success">
|
||||
Decrypt & Submit</button>
|
||||
Send Partial Decryptions</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Information Dialog called upon request -->
|
||||
<div class="modal fade" id="modalDialog" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal fade" id="modalDialog" role="dialog" tabindex="-1" data-backdrop="static">
|
||||
<div class="modal-dialog" role="document">
|
||||
|
||||
<!-- Dialog content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title" style="text-align: center"><strong>Thank You</strong></h4>
|
||||
<h4 class="modal-title" style="text-align: center"><strong>Partial Decryption Successfully Received</strong></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Thank you! You can now close down this page.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<h4>Key Generation For: {{ user_email }}</h4>
|
||||
<br/>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>Step 1: Generate Your Secret Key</strong></div>
|
||||
<div class="panel-heading"><strong>Step 1: Generate and Download Your Secret Key</strong></div>
|
||||
<div class="panel panel-body">
|
||||
<input id="secret-key" class="textinput textInput form-control" type="text"/>
|
||||
<input id="event-param" type="text" value="{{event.EID}}" hidden/>
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
// -------------- Global vars --------------------
|
||||
var filesHandleSK = document.getElementById('files_sk_upload');
|
||||
var CSRF = $( "input[name='csrfmiddlewaretoken']" ).val();
|
||||
|
||||
// -------------- Helper fns --------------------
|
||||
//SK checking algorithm - If PK and SK matches, it returns True; otherwise, it returns false.
|
||||
// Written by Bingsheng Zhang
|
||||
function skCheck(ctx, params, SK, PK) {
|
||||
|
@ -5,6 +10,29 @@ function skCheck(ctx, params, SK, PK) {
|
|||
return D.equals(PK)
|
||||
}
|
||||
|
||||
function csrfSafeMethod(method) {
|
||||
// these HTTP methods do not require CSRF protection
|
||||
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
|
||||
}
|
||||
|
||||
function showDialog(titleTxt, bodyTxt) {
|
||||
var modalDialog = $('#modalDialog');
|
||||
var title = modalDialog.find('.modal-title');
|
||||
var body = modalDialog.find('.modal-body');
|
||||
|
||||
title.text(titleTxt);
|
||||
var bodyText = bodyTxt;
|
||||
|
||||
var p = document.createElement("p");
|
||||
p.innerHTML = bodyText;
|
||||
body.empty();
|
||||
body.append( p );
|
||||
|
||||
modalDialog.modal('show');
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
|
||||
function validateSKFromString(SKStr) {
|
||||
// Re-create the SK from the string byte definition
|
||||
let ctx = new CTX("BN254CX");
|
||||
|
@ -38,20 +66,44 @@ function validateSKFromString(SKStr) {
|
|||
return skCheck(ctx, params, sk, pk);
|
||||
}
|
||||
|
||||
function showDialog(titleTxt, bodyTxt) {
|
||||
var modalDialog = $('#modalDialog');
|
||||
var title = modalDialog.find('.modal-title');
|
||||
var body = modalDialog.find('.modal-body');
|
||||
function decryptSubmitCiphers() {
|
||||
var skString = $('#secret-key').val();
|
||||
|
||||
title.text(titleTxt);
|
||||
var bodyText = bodyTxt;
|
||||
if (!skString) {
|
||||
showDialog('Error', 'You haven\'t supplied your secret key. Please go back and upload this from file.');
|
||||
}
|
||||
else {
|
||||
// Rebuild the trustee's secret key
|
||||
var ctx = new CTX("BN254CX");
|
||||
var skBytes = skString.split(",");
|
||||
var sk = new ctx.BIG.fromBytes(skBytes);
|
||||
|
||||
var p = document.createElement("p");
|
||||
p.innerHTML = bodyText;
|
||||
body.empty();
|
||||
body.append( p );
|
||||
var inputs = $("form input[type=text]");
|
||||
|
||||
modalDialog.modal('show');
|
||||
inputs.each(function() { //for each ciphertext to decrypt
|
||||
let input = $(this);
|
||||
console.log(input.attr('name'));
|
||||
|
||||
var ciphertext = {
|
||||
C1: null,
|
||||
C2: null
|
||||
};
|
||||
|
||||
var temp = JSON.parse(input.val());
|
||||
var c1Bytes = getBytes(temp.C1.split(','));
|
||||
ciphertext.C1 = new ctx.ECP.fromBytes(c1Bytes);
|
||||
|
||||
var c2Bytes = getBytes(temp.C2.split(','));
|
||||
ciphertext.C2 = new ctx.ECP.fromBytes(c2Bytes);
|
||||
|
||||
// Perform partial decryption where the method returns an object containing an ECP()
|
||||
var partial = partDec(sk, ciphertext);
|
||||
|
||||
var bytes = [];
|
||||
partial.D.toBytes(bytes);
|
||||
input.val(bytes.toString());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function processFileSKChange(event) {
|
||||
|
@ -85,8 +137,6 @@ function processFileSKChange(event) {
|
|||
}
|
||||
}
|
||||
|
||||
var filesHandleSK = document.getElementById('files_sk_upload');
|
||||
|
||||
if(filesHandleSK) {
|
||||
filesHandleSK.addEventListener('change', processFileSKChange, false);
|
||||
}
|
Reference in a new issue