From bd9c35102ef89e15d73a4998b257b3354a6bed67 Mon Sep 17 00:00:00 2001 From: vince0656 Date: Wed, 5 Sep 2018 11:46:57 +0100 Subject: [PATCH] The generic functionality of the event_vote page has been replicated in the sense that if access is denied to event_setup or event_decrypt, a client is not directed to a page that they don't have access to. Instead, they're kept on the same page and are told why access to that page has been denied. Furthermore, the POSTing of data to the back-end on both of these updated page is now done using Ajax requests where dialogs update the user rather than redirecting to a page the user doesn't have access to. --- allauthdemo/polls/views.py | 44 ++++-- .../templates/bases/bootstrap-jquery.html | 68 +-------- .../templates/polls/event_decrypt.html | 137 ++++++++++-------- allauthdemo/templates/polls/event_setup.html | 84 +++++++---- static/js/decrypt_event.js | 51 ++++++- static/js/event_setup.js | 105 ++++++++++++++ 6 files changed, 311 insertions(+), 178 deletions(-) create mode 100644 static/js/event_setup.js diff --git a/allauthdemo/polls/views.py b/allauthdemo/polls/views.py index cd0a48f..562a980 100755 --- a/allauthdemo/polls/views.py +++ b/allauthdemo/polls/views.py @@ -253,14 +253,16 @@ def event_trustee_setup(request, event_id): email_key = event.keys.filter(key=access_key) if email_key.exists() and event.users_trustees.filter(email=email_key[0].user.email).exists(): if TrusteeKey.objects.filter(event=event, user=email_key[0].user).exists(): - messages.add_message(request, messages.WARNING, 'You have already submitted your key for this event') - return HttpResponseRedirect(reverse("user_home")) + return render(request, "polls/event_setup.html", {"is_trustee": True, + "can_submit": False, + "access_denied_reason": "You have already submitted your public key for this event. Thank you!" + }) if request.method == "POST": form = EventSetupForm(request.POST) # If form data is valid, create a TrusteeKey object with the supplied public key if form.is_valid(): - public_key = request.POST["public_key"] + public_key = request.POST.get("public_key") key = TrusteeKey.objects.get_or_create(event=event, user=email_key[0].user)[0] key.key = public_key key.save() @@ -280,11 +282,18 @@ def event_trustee_setup(request, event_id): return HttpResponseRedirect(reverse("user_home")) else: form = EventSetupForm() - return render(request, "polls/event_setup.html", {"event": event, "form": form, "user_email": email_key[0].user.email}) + return render(request, "polls/event_setup.html", {"event": event, + "form": form, + "user_email": email_key[0].user.email, + "is_trustee": True, + "can_submit": True + }) - #if no key or is invalid? - messages.add_message(request, messages.WARNING, 'You do not have permission to access: ' + request.path) - return HttpResponseRedirect(reverse("user_home")) + else: + return render(request, "polls/event_setup.html", {"is_trustee": False, + "can_submit": False, + "access_denied_reason": "You do not have permission to access this page." + }) def event_end(request, event_id): @@ -311,11 +320,10 @@ def event_trustee_decrypt(request, event_id): if email_key.exists() and event.users_trustees.filter(email=trustee.email).exists(): if PartialBallotDecryption.objects.filter(event=event, user=trustee).count() == event.total_num_opts(): - - warning_msg = 'You have already provided your decryption key for this event - Thank You' - messages.add_message(request, messages.WARNING, warning_msg) - - return HttpResponseRedirect(reverse("user_home")) + return render(request, "polls/event_decrypt.html", {"is_trustee": True, + "can_submit": False, + "access_denied_reason": "You have already submitted your partial decryptions for this event. Thank you!" + }) elif request.method == "GET": # Get the Trustee's original PK - used in the template for SK validation trustee_pk = TrusteeKey.objects.get(event=event, user=trustee).key @@ -344,7 +352,9 @@ def event_trustee_decrypt(request, event_id): "event": event, "user_email": trustee.email, "trustee_pk": trustee_pk, - "poll_ciphers": poll_ciphers + "poll_ciphers": poll_ciphers, + "is_trustee": True, + "can_submit": True }) elif request.method == "POST": @@ -359,7 +369,7 @@ def event_trustee_decrypt(request, event_id): input_name = str("") input_name = "poll-" + str(i) + "-cipher-" + str(j) - part_dec = request.POST[input_name] + part_dec = request.POST.get(input_name) PartialBallotDecryption.objects.create(event=event, poll=polls[i], @@ -381,8 +391,10 @@ def event_trustee_decrypt(request, event_id): return HttpResponseRedirect(reverse("user_home")) # Without an access key, the client does not have permission to access this page - messages.add_message(request, messages.WARNING, 'You do not have permission to decrypt this Event.') - return HttpResponseRedirect(reverse("user_home")) + return render(request, "polls/event_decrypt.html", {"is_trustee": False, + "can_submit": False, + "access_denied_reason": "You don't have permission to access this page." + }) def manage_questions(request, event_id): diff --git a/allauthdemo/templates/bases/bootstrap-jquery.html b/allauthdemo/templates/bases/bootstrap-jquery.html index 59279ac..a4679ac 100755 --- a/allauthdemo/templates/bases/bootstrap-jquery.html +++ b/allauthdemo/templates/bases/bootstrap-jquery.html @@ -22,6 +22,7 @@ + @@ -66,73 +67,6 @@ } - function getBytes(arr) { - for(var i = 0; i < arr.length; i++) { - arr[i] = parseInt(arr[i]); - } - - return new Uint8Array(arr); - } - - //new function - demosEncrypt.decryptSubmitCiphers = function() { - - }; - - //new function - demosEncrypt.generateKeys = function() { - var parameter = $("#event-param").val(); - var tempParams = JSON.parse(JSON.parse(parameter).crypto); - //the full objects need to be initalised as per the library, then copy the values we need into it - //I follow Bingsheng's code as to what objects are used in the parameter object - var ctx = new CTX("BN254CX"); //new context we can use - var n = new ctx.BIG(); - var g1 = new ctx.ECP(); - var g2 = new ctx.ECP2(); - - //copying the values - n.copy(tempParams.n); - g1.copy(tempParams.g1); - g2.copy(tempParams.g2); - - var params = { - n:n, - g1:g1, - g2:g2 - } - - var PKbytes = []; - var SKbytes = []; - - var keypair = keyGen(params); - keypair.PK.toBytes(PKbytes); - keypair.SK.toBytes(SKbytes); - - var PKB64Encoded = ""; - for(let i = 0; i < PKbytes.length; i++) { - PKB64Encoded += btoa(PKbytes[i]); - } - - var SKB64Encoded = ""; - for(let j = 0; j < SKbytes.length; j++) { - SKB64Encoded += btoa(SKbytes[j]); - } - - $('input#public-key').val(PKB64Encoded); - $('input#secret-key').val(SKB64Encoded); - - //mostly code from before here - var blob = new Blob([SKB64Encoded], {type : 'text/plain'}); - - var dlBtn = $('a#download-btn'); - var url = URL.createObjectURL(blob); - var fileName = $(dlBtn).attr("href", url); - - $(dlBtn).attr("download", "sk-{% block sk-file-name %}{% endblock %}".replace(/[\W]/g, "-")); - $(dlBtn).attr("disabled", false); - $("#public-submit").attr("disabled", false); - } - //these other functions might not be used //I don't think this is used demosEncrypt.downloadSecretKey = function() { diff --git a/allauthdemo/templates/polls/event_decrypt.html b/allauthdemo/templates/polls/event_decrypt.html index 808011e..d2959ad 100755 --- a/allauthdemo/templates/polls/event_decrypt.html +++ b/allauthdemo/templates/polls/event_decrypt.html @@ -4,77 +4,86 @@ {% block sk-file-name %}{{ event.title|safe }}{% endblock %} {% block content %} - +{% if is_trustee and can_submit %} + + +
+

Trustee Event Decryption for Event '{{ event.title }}'

+
+
+
Upload your Secret Key as '{{ user_email }}'
+
+ + + +
- - -
-
-
Encrypted Event Data
-
-
- {% csrf_token %} - {% for opts_ciphers in poll_ciphers %} - {% for cipher in opts_ciphers %} - +
+
Encrypted Event Data
+
+ + {% csrf_token %} + {% for opts_ciphers in poll_ciphers %} + {% for cipher in opts_ciphers %} + +
+ {% endfor %}
{% endfor %} -
- {% endfor %} - - + + +
-
- - - - + + + +{% else %} +
+ +
+{% endif %} {% endblock %} diff --git a/allauthdemo/templates/polls/event_setup.html b/allauthdemo/templates/polls/event_setup.html index 81d4ce5..87be9f8 100755 --- a/allauthdemo/templates/polls/event_setup.html +++ b/allauthdemo/templates/polls/event_setup.html @@ -6,34 +6,66 @@ {% block content %} -
-

Trustee Event Setup for Event '{{ event.title }}'

-
-

Key Generation For: {{ user_email }}

-
-
-
Step 1: Generate and Download Your Secret Key
-
- - - -
-
Step 2: Submit Your Public Key
-
- {% load crispy_forms_tags %} -
- {% crispy form %} -

Ensure your secret key is backed up before submitting.

- -
+ + + -
-
+{% else %} +
+ +
+{% endif %} {% endblock %} diff --git a/static/js/decrypt_event.js b/static/js/decrypt_event.js index 5255fa7..1563d72 100644 --- a/static/js/decrypt_event.js +++ b/static/js/decrypt_event.js @@ -1,6 +1,7 @@ // -------------- Global vars -------------------- var filesHandleSK = document.getElementById('files_sk_upload'); var CSRF = $( "input[name='csrfmiddlewaretoken']" ).val(); +var partialDecryptions = {}; // -------------- Helper fns -------------------- //SK checking algorithm - If PK and SK matches, it returns True; otherwise, it returns false. @@ -15,6 +16,14 @@ function csrfSafeMethod(method) { return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } +function getBytes(arr) { + for(var i = 0; i < arr.length; i++) { + arr[i] = parseInt(arr[i]); + } + + return new Uint8Array(arr); +} + function getKeyBytes(key, byteArray) { for(let i = 0; i < key.length; i += 4) { let B64EncodedByte = key.substring(i, i + 4); @@ -23,8 +32,8 @@ function getKeyBytes(key, byteArray) { } } -function showDialog(titleTxt, bodyTxt) { - var modalDialog = $('#modalDialog'); +function showDecryptDialog(titleTxt, bodyTxt) { + var modalDialog = $('#EventDecryptionModalDialog'); var title = modalDialog.find('.modal-title'); var body = modalDialog.find('.modal-body'); @@ -79,7 +88,7 @@ function validateSKFromString(SKStr) { return skCheck(ctx, params, sk, pk); } -function decryptSubmitCiphers() { +function decryptSubmit() { var skString = $('#secret-key').val(); if (!skString) { @@ -96,7 +105,6 @@ function decryptSubmitCiphers() { inputs.each(function() { //for each ciphertext to decrypt let input = $(this); - console.log(input.attr('name')); var ciphertext = { C1: null, @@ -116,10 +124,38 @@ function decryptSubmitCiphers() { var bytes = []; partial.D.toBytes(bytes); input.val(bytes.toString()); + + partialDecryptions[input.attr('name')] = bytes.toString(); }); + + submitPartialDecryptions(); } } +function onAfterPartialDecryptionsSend() { + showDecryptDialog('Partial Decryptions Successfully Received', + 'Thank you! You can now close down this page.'); +} + +function submitPartialDecryptions() { + $.ajaxSetup({ + beforeSend: function(xhr, settings) { + if (!csrfSafeMethod(settings.type) && !this.crossDomain) { + xhr.setRequestHeader("X-CSRFToken", CSRF); + } + } + }); + + $.ajax({ + type : "POST", + url : window.location, + data : partialDecryptions, + success : function(){ + onAfterPartialDecryptionsSend(); + } + }); +} + function processFileSKChange(event) { var files = event.target.files; @@ -153,4 +189,9 @@ function processFileSKChange(event) { if(filesHandleSK) { filesHandleSK.addEventListener('change', processFileSKChange, false); -} \ No newline at end of file +} + +$('#EventDecryptionModalDialog').on('hide.bs.modal', function (e) { + // Update page to reflect the fact that the PK submission has taken place + location.reload(); +}); \ No newline at end of file diff --git a/static/js/event_setup.js b/static/js/event_setup.js new file mode 100644 index 0000000..c91ac2c --- /dev/null +++ b/static/js/event_setup.js @@ -0,0 +1,105 @@ +var CSRF = $( "input[name='csrfmiddlewaretoken']" ).val(); + +function csrfSafeMethod(method) { + // these HTTP methods do not require CSRF protection + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); +} + +function showSetupDialog(titleTxt, bodyTxt) { + var modalDialog = $('#EventSetupModalDialog'); + 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 generateKeys() { + var parameter = $("#event-param").val(); + var tempParams = JSON.parse(JSON.parse(parameter).crypto); + //the full objects need to be initalised as per the library, then copy the values we need into it + //I follow Bingsheng's code as to what objects are used in the parameter object + var ctx = new CTX("BN254CX"); //new context we can use + var n = new ctx.BIG(); + var g1 = new ctx.ECP(); + var g2 = new ctx.ECP2(); + + //copying the values + n.copy(tempParams.n); + g1.copy(tempParams.g1); + g2.copy(tempParams.g2); + + var params = { + n:n, + g1:g1, + g2:g2 + } + + var PKbytes = []; + var SKbytes = []; + + var keypair = keyGen(params); + keypair.PK.toBytes(PKbytes); + keypair.SK.toBytes(SKbytes); + + var PKB64Encoded = ""; + for(let i = 0; i < PKbytes.length; i++) { + PKB64Encoded += btoa(PKbytes[i]); + } + + var SKB64Encoded = ""; + for(let j = 0; j < SKbytes.length; j++) { + SKB64Encoded += btoa(SKbytes[j]); + } + + $('input#public-key').val(PKB64Encoded); + $('input#secret-key').val(SKB64Encoded); + + //mostly code from before here + var blob = new Blob([SKB64Encoded], {type : 'text/plain'}); + + var dlBtn = $('a#download-btn'); + var url = URL.createObjectURL(blob); + $(dlBtn).attr("href", url); + + let fileName = "sk-" + EVENT_TITLE.replace(/[\W]/g, "-"); + $(dlBtn).attr("download", fileName); + $(dlBtn).attr("disabled", false); + $("#public-submit").attr("disabled", false); +} + +function onAfterKeySend() { + showSetupDialog('Public Key Successfully Received', + 'Thank you! You can now close down this page.'); +} + +function submitPublicKey() { + $.ajaxSetup({ + beforeSend: function(xhr, settings) { + if (!csrfSafeMethod(settings.type) && !this.crossDomain) { + xhr.setRequestHeader("X-CSRFToken", CSRF); + } + } + }); + + $.ajax({ + type : "POST", + url : window.location, + data : { public_key: $('input#public-key').val() }, + success : function(){ + onAfterKeySend(); + } + }); +} + +$('#EventSetupModalDialog').on('hide.bs.modal', function (e) { + // Update page to reflect the fact that the PK submission has taken place + location.reload(); +}); \ No newline at end of file