From 08b59853966567c9c46269f23871f27a1ca94d02 Mon Sep 17 00:00:00 2001 From: Rumperuu Date: Wed, 19 Sep 2018 08:38:50 +0100 Subject: [PATCH] ajg --- allauthdemo/polls/models.py | 298 +++---- allauthdemo/polls/urls.py | 3 +- allauthdemo/polls/views.py | 814 +++++++++--------- .../templates/bases/bootstrap-jquery.html | 1 + allauthdemo/templates/polls/find_ballot.html | 11 + allauthdemo/templates/polls/vote_audit.html | 36 +- static/js/event_vote.js | 60 +- static/js/find_ballot.js | 10 + static/js/vote_audit.js | 33 +- 9 files changed, 704 insertions(+), 562 deletions(-) create mode 100644 allauthdemo/templates/polls/find_ballot.html create mode 100644 static/js/find_ballot.js diff --git a/allauthdemo/polls/models.py b/allauthdemo/polls/models.py index 9a51427..7b5bf34 100755 --- a/allauthdemo/polls/models.py +++ b/allauthdemo/polls/models.py @@ -11,223 +11,223 @@ from allauthdemo.auth.models import DemoUser class EmailUser(models.Model): - email = models.CharField(max_length=80, unique=True) + email = models.CharField(max_length=80, unique=True) - def send_email(self, subject, message, from_email=None): - """ - Sends an email to this User. - """ - send_mail(subject, message, from_email, [self.email]) + def send_email(self, subject, message, from_email=None): + """ + Sends an email to this User. + """ + send_mail(subject, message, from_email, [self.email]) - def __unicode__(self): - return self.email + def __unicode__(self): + return self.email class Event(models.Model): - users_organisers = models.ManyToManyField(DemoUser, blank=True, related_name="organisers") - users_trustees = models.ManyToManyField(EmailUser, blank=True, related_name="trustees") - voters = models.ManyToManyField(EmailUser, blank=True, related_name="voters") - start_time = models.DateTimeField() - end_time = models.DateTimeField() - prepared = models.BooleanField(default=False) - ended = models.BooleanField(default=False) - public_key = models.CharField(null=True, blank=False, max_length=1024) - title = models.CharField(max_length=1024) - EID = models.CharField(max_length=2048, blank=True) - creator = models.CharField(max_length=256, blank=True) - c_email = models.CharField(max_length=512, blank=True) - trustees = models.CharField(max_length=4096) - uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + users_organisers = models.ManyToManyField(DemoUser, blank=True, related_name="organisers") + users_trustees = models.ManyToManyField(EmailUser, blank=True, related_name="trustees") + voters = models.ManyToManyField(EmailUser, blank=True, related_name="voters") + start_time = models.DateTimeField() + end_time = models.DateTimeField() + prepared = models.BooleanField(default=False) + ended = models.BooleanField(default=False) + public_key = models.CharField(null=True, blank=False, max_length=1024) + title = models.CharField(max_length=1024) + EID = models.CharField(max_length=2048, blank=True) + creator = models.CharField(max_length=256, blank=True) + c_email = models.CharField(max_length=512, blank=True) + trustees = models.CharField(max_length=4096) + uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - # Custom helper methods - def EID_hr(self): - try: - EID_json = json.loads(self.EID) - return EID_json['hr'] - except ValueError: - return self.EID + # Custom helper methods + def EID_hr(self): + try: + EID_json = json.loads(self.EID) + return EID_json['hr'] + except ValueError: + return self.EID - def EID_crypto(self): - try: - EID_json = json.loads(self.EID) - EID_crypto_str = EID_json['crypto'] - return json.dumps(json.loads(EID_crypto_str)) - except ValueError: - return "None - Event not Initialised" + def EID_crypto(self): + try: + EID_json = json.loads(self.EID) + EID_crypto_str = EID_json['crypto'] + return json.dumps(json.loads(EID_crypto_str)) + except ValueError: + return "None - Event not Initialised" - def duration(self): - duration_str = self.start_time_formatted() - duration_str = duration_str + " - " + self.end_time_formatted_utc() - return duration_str + def duration(self): + duration_str = self.start_time_formatted() + duration_str = duration_str + " - " + self.end_time_formatted_utc() + return duration_str - def start_time_formatted(self): - return self.start_time.strftime("%d-%m-%y %H:%M") + def start_time_formatted(self): + return self.start_time.strftime("%d-%m-%y %H:%M") - def start_time_formatted_utc(self): - return self.start_time.strftime("%d-%m-%y %H:%M %Z") + def start_time_formatted_utc(self): + return self.start_time.strftime("%d-%m-%y %H:%M %Z") - def end_time_formatted(self): - return self.end_time.strftime("%d-%m-%y %H:%M") + def end_time_formatted(self): + return self.end_time.strftime("%d-%m-%y %H:%M") - def end_time_formatted_utc(self): - return self.end_time.strftime("%d-%m-%y %H:%M %Z") + def end_time_formatted_utc(self): + return self.end_time.strftime("%d-%m-%y %H:%M %Z") - # Total number of options in all polls - def total_num_opts(self): - polls = self.polls.all() - count = 0 + # Total number of options in all polls + def total_num_opts(self): + polls = self.polls.all() + count = 0 - for poll in polls: - count += poll.options.all().count() + for poll in polls: + count += poll.options.all().count() - return count + return count - def total_num_partial_decs(self): - polls = self.polls.all() - count = 0 + def total_num_partial_decs(self): + polls = self.polls.all() + count = 0 - for poll in polls: - count += PartialBallotDecryption.objects.filter(poll=poll).count() + for poll in polls: + count += PartialBallotDecryption.objects.filter(poll=poll).count() - return count + return count - def all_part_decs_received(self): - received = False + def all_part_decs_received(self): + received = False - if self.total_num_partial_decs() == self.total_num_opts() * self.users_trustees.all().count(): - received = True + if self.total_num_partial_decs() == self.total_num_opts() * self.users_trustees.all().count(): + received = True - return received + return received - def status(self): - status_str = "" + def status(self): + status_str = "" - # Get the current date and time to compare against to establish if this is a past, current or - # future event. Prepared means the public key has been initialised - present = timezone.now() + # Get the current date and time to compare against to establish if this is a past, current or + # future event. Prepared means the public key has been initialised + present = timezone.now() - if self.ended is False: - if present < self.start_time and self.prepared is False: - status_str = "Future" - elif present < self.start_time and self.prepared is True: - status_str = "Prepared" - elif present >= self.start_time and present <= self.end_time and self.prepared is True: - status_str = "Active" - elif present >= self.start_time and present <= self.end_time and self.prepared is False: - status_str = "Future" - elif present > self.end_time: - status_str = "Expired" - else: - if self.all_part_decs_received(): - status_str = "Decrypted" - else: - status_str = "Ended" + if self.ended is False: + if present < self.start_time and self.prepared is False: + status_str = "Future" + elif present < self.start_time and self.prepared is True: + status_str = "Prepared" + elif present >= self.start_time and present <= self.end_time and self.prepared is True: + status_str = "Active" + elif present >= self.start_time and present <= self.end_time and self.prepared is False: + status_str = "Future" + elif present > self.end_time: + status_str = "Expired" + else: + if self.all_part_decs_received(): + status_str = "Decrypted" + else: + status_str = "Ended" - return status_str + return status_str - ''' - The result applies to all polls for an event so True will only be returned when votes have - been received for every poll. - ''' - def has_received_votes(self): - received_votes = True + ''' + The result applies to all polls for an event so True will only be returned when votes have + been received for every poll. + ''' + def has_received_votes(self): + received_votes = True - for poll in self.polls.all(): - if Ballot.objects.filter(poll=poll, cast=True).count() == 0: - received_votes = False + for poll in self.polls.all(): + if Ballot.objects.filter(poll=poll, cast=True).count() == 0: + received_votes = False - return received_votes + return received_votes - def __str__(self): - return self.title + def __str__(self): + return self.title class TrusteeKey(models.Model): - event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="trustee_keys") - user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="trustee_keys") - key = models.CharField(max_length=260) + event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="trustee_keys") + user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="trustee_keys") + key = models.CharField(max_length=260) class AccessKey(models.Model): - event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="keys") - user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="keys") - key = models.CharField(max_length=255, unique=True) + event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="keys") + user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="keys") + key = models.CharField(max_length=255, unique=True) - def has_started(self): - return timezone.now() >= self.start + def has_started(self): + return timezone.now() >= self.start - def has_ended(self): - return timezone.now() >= self.end + def has_ended(self): + return timezone.now() >= self.end - def __unicode__(self): - return self.title + def __unicode__(self): + return self.title class Poll(models.Model): - question_text = models.CharField(max_length=200) - total_votes = models.IntegerField(default=0) - min_num_selections = models.IntegerField(default=0) - max_num_selections = models.IntegerField(default=1) - event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="polls") - combined_ballots = models.CharField(max_length=4096, null=True) - result_json = models.CharField(max_length=4096, null=True) - uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + question_text = models.CharField(max_length=200) + total_votes = models.IntegerField(default=0) + min_num_selections = models.IntegerField(default=0) + max_num_selections = models.IntegerField(default=1) + event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="polls") + combined_ballots = models.CharField(max_length=4096, null=True) + result_json = models.CharField(max_length=4096, null=True) + uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - def __str__(self): - return self.question_text + def __str__(self): + return self.question_text class PollOption(models.Model): - choice_text = models.CharField(max_length=200) - votes = models.IntegerField(default=0) - question = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="options") + choice_text = models.CharField(max_length=200) + votes = models.IntegerField(default=0) + question = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="options") - def __str__(self): - return self.choice_text + def __str__(self): + return self.choice_text class CombinedBallot(models.Model): - poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="combined_ballot") - option = models.ForeignKey(PollOption, on_delete=models.CASCADE, related_name="combined_ballot") - cipher_text_c1 = models.CharField(max_length=4096) - cipher_text_c2 = models.CharField(max_length=4096) + poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="combined_ballot") + option = models.ForeignKey(PollOption, on_delete=models.CASCADE, related_name="combined_ballot") + cipher_text_c1 = models.CharField(max_length=4096) + cipher_text_c2 = models.CharField(max_length=4096) # A partial decryption supplied by a trustee for a combined ballot that relates to a poll option class PartialBallotDecryption(models.Model): - event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="decryption") - poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="decryption") - option = models.ForeignKey(PollOption, on_delete=models.CASCADE, related_name="decryption") - user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="decryption") - text = models.CharField(max_length=4096) + event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="decryption") + poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="decryption") + option = models.ForeignKey(PollOption, on_delete=models.CASCADE, related_name="decryption") + user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="decryption") + text = models.CharField(max_length=4096) 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) + 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) class EncBallot(models.Model): - handle = models.CharField(primary_key=True, default=uuid.uuid4, editable=False, max_length=255) - ballot = models.CharField(max_length=10240) + handle = models.CharField(primary_key=True, default=uuid.uuid4, editable=False, max_length=255) + ballot = models.CharField(max_length=10240) # Implements the new binary encoding scheme class EncryptedVote(models.Model): - ballot = models.ForeignKey(Ballot, on_delete=models.CASCADE, related_name="encrypted_vote") + ballot = models.ForeignKey(Ballot, on_delete=models.CASCADE, related_name="encrypted_vote") class CombinedEncryptedVote(models.Model): - ballot = models.ForeignKey(Ballot, on_delete=models.CASCADE, related_name="comb_encrypted_vote") + ballot = models.ForeignKey(Ballot, on_delete=models.CASCADE, related_name="comb_encrypted_vote") class VoteFragment(models.Model): - encrypted_vote = models.ForeignKey(EncryptedVote, on_delete=models.CASCADE, related_name="fragment", null=True) - comb_encrypted_vote = models.ForeignKey(CombinedEncryptedVote, on_delete=models.CASCADE, related_name="fragment", null=True) - cipher_text_c1 = models.CharField(max_length=4096) - cipher_text_c2 = models.CharField(max_length=4096) + encrypted_vote = models.ForeignKey(EncryptedVote, on_delete=models.CASCADE, related_name="fragment", null=True) + comb_encrypted_vote = models.ForeignKey(CombinedEncryptedVote, on_delete=models.CASCADE, related_name="fragment", null=True) + cipher_text_c1 = models.CharField(max_length=4096) + cipher_text_c2 = models.CharField(max_length=4096) diff --git a/allauthdemo/polls/urls.py b/allauthdemo/polls/urls.py index 2e9c840..fe76246 100755 --- a/allauthdemo/polls/urls.py +++ b/allauthdemo/polls/urls.py @@ -21,5 +21,6 @@ urlpatterns = [ url(r'^(?P[0-9a-f-]+)/poll/(?P[0-9a-f-]+)/vote/$', views.event_vote, name='event-vote'), url(r'^(?P[0-9a-f-]+)/create/poll/$', login_required(views.manage_questions), name='create-poll'), url(r'^(?P[0-9a-f-]+)/poll/(?P[0-9a-f-]+)/edit$', login_required(views.edit_poll), name='edit-poll'), - url(r'^audit/$', views.vote_audit, name='vote_audit') + url(r'^audit/$', views.vote_audit, name='vote_audit'), + url(r'^find_ballot/$', views.find_ballot, name='find_ballot') ] diff --git a/allauthdemo/polls/views.py b/allauthdemo/polls/views.py index 562a980..a3279f6 100755 --- a/allauthdemo/polls/views.py +++ b/allauthdemo/polls/views.py @@ -25,529 +25,559 @@ from .utils.EventModelAdaptor import EventModelAdaptor class EventListView(generic.ListView): - model = Event + model = Event - def get_context_data(self, **kwargs): - context = super(EventListView, self).get_context_data(**kwargs) - return context + def get_context_data(self, **kwargs): + context = super(EventListView, self).get_context_data(**kwargs) + return context class EventDetailView(generic.DetailView): - template_name = "polls/event_detail_details.html" - model = Event + template_name = "polls/event_detail_details.html" + model = Event - def get_context_data(self, **kwargs): - context = super(EventDetailView, self).get_context_data(**kwargs) - context['is_organiser'] = (not self.request.user.is_anonymous()) and (self.object.users_organisers.filter(email=self.request.user.email).exists()) - context['decrypted'] = self.object.status() == "Decrypted" + def get_context_data(self, **kwargs): + context = super(EventDetailView, self).get_context_data(**kwargs) + context['is_organiser'] = (not self.request.user.is_anonymous()) and (self.object.users_organisers.filter(email=self.request.user.email).exists()) + context['decrypted'] = self.object.status() == "Decrypted" - # Get the results for all polls - polls = self.object.polls.all() + # Get the results for all polls + polls = self.object.polls.all() - results = list() - for poll in polls: - result_json = poll.result_json + results = list() + for poll in polls: + result_json = poll.result_json - if result_json is None: - continue + if result_json is None: + continue - if result_json[len(result_json)-1] == ',': - result_json = result_json[0:len(result_json)-1] + if result_json[len(result_json)-1] == ',': + result_json = result_json[0:len(result_json)-1] - result_json = json.loads(result_json) - results.append(result_json) + result_json = json.loads(result_json) + results.append(result_json) - context['event_results'] = results + context['event_results'] = results - return context + return context class EventDetailPollsView(EventDetailView): - template_name = "polls/event_detail_polls.html" + template_name = "polls/event_detail_polls.html" class EventDetailEntitiesView(EventDetailView): - template_name = "polls/event_detail_entities.html" + template_name = "polls/event_detail_entities.html" class EventDetailAdvancedView(EventDetailView): - template_name = "polls/event_detail_advanced.html" + template_name = "polls/event_detail_advanced.html" class PollDetailView(generic.View): - model = Poll + model = Poll - def get_context_data(self, **kwargs): - context = super(PollDetailView, self).get_context_data(**kwargs) - context['form'] = VoteForm(instance=self.object) - context['poll_count'] = self.object.event.polls.all().count() - return context + def get_context_data(self, **kwargs): + context = super(PollDetailView, self).get_context_data(**kwargs) + context['form'] = VoteForm(instance=self.object) + context['poll_count'] = self.object.event.polls.all().count() + return context class EventDetailResultsView(EventDetailView): - template_name = "polls/event_results.html" + template_name = "polls/event_results.html" def util_get_poll_by_event_index(event, poll_id): - return event.polls.get(uuid=poll_id) + return event.polls.get(uuid=poll_id) def edit_poll(request, event_id, poll_id): - event = get_object_or_404(Event, pk=event_id) - poll = util_get_poll_by_event_index(event, poll_id) + event = get_object_or_404(Event, pk=event_id) + poll = util_get_poll_by_event_index(event, poll_id) - if (poll == None): - raise Http404("Poll does not exist") + if (poll == None): + raise Http404("Poll does not exist") - if request.method == 'GET': - form = PollForm(instance=poll, prefix="main") - formset = OptionFormset(instance=poll, prefix="formset_options") - return render(request, "polls/generic_form.html", {'form_title': "Edit Poll: " + poll.question_text, 'form': form, 'option_formset': formset}) - elif request.method == 'POST': - form = PollForm(request.POST, instance=poll, prefix="main") + if request.method == 'GET': + form = PollForm(instance=poll, prefix="main") + formset = OptionFormset(instance=poll, prefix="formset_options") + return render(request, "polls/generic_form.html", {'form_title': "Edit Poll: " + poll.question_text, 'form': form, 'option_formset': formset}) + elif request.method == 'POST': + form = PollForm(request.POST, instance=poll, prefix="main") - if form.is_valid(): - form.save() + if form.is_valid(): + form.save() - formset = OptionFormset(request.POST, instance=poll, prefix="formset_options") + formset = OptionFormset(request.POST, instance=poll, prefix="formset_options") - if formset.is_valid(): - formset.save() - return HttpResponseRedirect(reverse('polls:event-polls', args=[poll.event_id])) + if formset.is_valid(): + formset.save() + return HttpResponseRedirect(reverse('polls:event-polls', args=[poll.event_id])) def vote_audit(request): - encrypted_ballot = get_object_or_404(EncBallot, handle=''+urllib.quote_plus(request.GET.get('handle', None))) + handle1 = request.GET.get('handle1', None) + handle2 = request.GET.get('handle2', None) + vars = {} + + if handle: + encrypted_ballot1 = get_object_or_404(EncBallot, handle=''+urllib.quote_plus(handle1)) + + if handle2: + encrypted_ballot2 = get_object_or_404(EncBallot, handle=''+urllib.quote_plus(handle2)) - return render(request, "polls/vote_audit.html", - { - "ballot": encrypted_ballot.ballot - }) + return render(request, "polls/vote_audit.html", + { + "handle1": handle1, + "ballot1": encrypted_ballot1.ballot, + "handle2": handle2, + "ballot2": encrypted_ballot2.ballot + }) + + +def find_ballot(request): + voter_id = request.GET.get('vid', None) + poll_id = request.GET.get('pid', None) + ballot = get_object_or_404(Ballot, voter=voter_id, poll=poll_id) + hash1 = request.GET.get('hash1', None) + hash2 = request.GET.get('hash2', None) + + if not ballot.cast: + messages.add_message(request, messages.WARNING, "This ballot has not been cast.") + return HttpResponseRedirect(reverse("user_home")) + + return render(request, "polls/find_ballot.html", + { + "ballot": ballot.json_str, + "hash1": hash1, + "hash2": hash2 + }) def event_vote(request, event_id, poll_id): - event = get_object_or_404(Event, pk=event_id) + event = get_object_or_404(Event, pk=event_id) - if not event.prepared: - messages.add_message(request, messages.WARNING, "This Event isn\'t ready for voting yet.") - return HttpResponseRedirect(reverse("user_home")) + if not event.prepared: + messages.add_message(request, messages.WARNING, "This Event isn\'t ready for voting yet.") + return HttpResponseRedirect(reverse("user_home")) - # Lookup the specified poll - poll = event.polls.get(uuid=poll_id) + # Lookup the specified poll + poll = event.polls.get(uuid=poll_id) - if poll is None: - messages.add_message(request, messages.ERROR, "There was an error loading the voting page.") - return HttpResponseRedirect(reverse("user_home")) + if poll is None: + messages.add_message(request, messages.ERROR, "There was an error loading the voting page.") + return HttpResponseRedirect(reverse("user_home")) - polls = event.polls.all() - event_poll_count = len(polls) - prev_poll_uuid, next_poll_uuid, poll_num = False, False, 0 - can_vote, cant_vote_reason, has_voted, voter_email = False, "", False, "" + polls = event.polls.all() + event_poll_count = len(polls) + prev_poll_uuid, next_poll_uuid, poll_num = False, False, 0 + can_vote, cant_vote_reason, has_voted, voter_email = False, "", False, "" - for i in range(event_poll_count): - poll = polls[i] - poll_uuid = str(poll.uuid) - req_poll_uuid = str(poll_id) + for i in range(event_poll_count): + poll = polls[i] + poll_uuid = str(poll.uuid) + req_poll_uuid = str(poll_id) - if poll_uuid == req_poll_uuid: - poll_num = str(i+1) + if poll_uuid == req_poll_uuid: + poll_num = str(i+1) - # If current voting request isn't for the last poll, then make sure we link to the next - if i != event_poll_count - 1: - # Only set the previous poll's uuid if we're not looking at the first poll - if i != 0: - prev_poll_uuid = str(polls[i - 1].uuid) + # If current voting request isn't for the last poll, then make sure we link to the next + if i != event_poll_count - 1: + # Only set the previous poll's uuid if we're not looking at the first poll + if i != 0: + prev_poll_uuid = str(polls[i - 1].uuid) - next_poll_uuid = str(polls[i + 1].uuid) - else: - if i != 0: - prev_poll_uuid = str(polls[i - 1].uuid) + next_poll_uuid = str(polls[i + 1].uuid) + else: + if i != 0: + prev_poll_uuid = str(polls[i - 1].uuid) - break + break - access_key = request.GET.get('key', None) - email_key = event.keys.filter(key=access_key) - email_key_str = email_key[0].key + access_key = request.GET.get('key', None) + email_key = event.keys.filter(key=access_key) + email_key_str = email_key[0].key - if email_key.exists() and event.voters.filter(email=email_key[0].user.email).exists(): - # Passing this test means the user can vote - voter_email = email_key[0].user.email - can_vote = True + if email_key.exists() and event.voters.filter(email=email_key[0].user.email).exists(): + # Passing this test means the user can vote + voter_email = email_key[0].user.email + can_vote = True - # Check whether this is the first time a user is voting - ballot = Ballot.objects.filter(voter=email_key[0].user, poll=poll) - if ballot.exists() and ballot[0].cast: - has_voted = True - else: - can_vote = False - cant_vote_reason = "You don't have permission to access this page." + # Check whether this is the first time a user is voting + ballot = Ballot.objects.filter(voter=email_key[0].user, poll=poll) + if ballot.exists() and ballot[0].cast: + has_voted = True + else: + can_vote = False + cant_vote_reason = "You don't have permission to access this page." - if event.status() != "Active": - can_vote = False - cant_vote_reason = "The event either isn't ready for voting or it has expired and therefore you cannot vote." + if event.status() != "Active": + can_vote = False + 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_str = request.POST.get('ballot') - ballot_json = json.loads(ballot_str) - selection = request.POST.get('selection') - encrypted_votes_json = ballot_json['encryptedVotes'] + if request.method == "POST": + ballot_str = request.POST.get('ballot') + ballot_json = json.loads(ballot_str) + selection = request.POST.get('selection') + encrypted_votes_json = ballot_json['encryptedVotes'] - enc_ballot_json = request.POST.get('encBallot') - handle_json = request.POST.get('handle') + enc_ballot_json = request.POST.get('encBallot') + handle_json = request.POST.get('handle') - # Adds or replaces the encrypted un-submitted ballot to the database for the auditor app to pick up later - if EncBallot.objects.filter(handle=handle_json).exists(): - b = EncBallot.objects.get(handle=handle_json) - b.ballot = enc_ballot_json - b.save() - else: - b = EncBallot(handle=handle_json, ballot=enc_ballot_json) - b.save() + # Adds or replaces the encrypted un-submitted ballot to the database for the auditor app to pick up later + if EncBallot.objects.filter(handle=handle_json).exists(): + b = EncBallot.objects.get(handle=handle_json) + b.ballot = enc_ballot_json + b.save() + else: + b = EncBallot(handle=handle_json, ballot=enc_ballot_json) + b.save() - # Before storing the encrypted votes, we need the voter's ballot - ballot, created = Ballot.objects.get_or_create(voter=email_key[0].user, poll=poll) - EncryptedVote.objects.filter(ballot=ballot).delete() + # Before storing the encrypted votes, we need the voter's ballot + ballot, created = Ballot.objects.get_or_create(voter=email_key[0].user, poll=poll) + EncryptedVote.objects.filter(ballot=ballot).delete() - for e_vote in encrypted_votes_json: - # Will store the fragments of the encoding scheme that define the vote - encrypted_vote = EncryptedVote.objects.create(ballot=ballot) - fragments_json = e_vote['fragments'] + for e_vote in encrypted_votes_json: + # Will store the fragments of the encoding scheme that define the vote + encrypted_vote = EncryptedVote.objects.create(ballot=ballot) + fragments_json = e_vote['fragments'] - for fragment in fragments_json: - VoteFragment.objects.create(encrypted_vote=encrypted_vote, - cipher_text_c1=fragment['C1'], - cipher_text_c2=fragment['C2']) + for fragment in fragments_json: + VoteFragment.objects.create(encrypted_vote=encrypted_vote, + cipher_text_c1=fragment['C1'], + cipher_text_c2=fragment['C2']) - ballot.cast = True - ballot.selection = selection - ballot.json_str = ballot_str - ballot.save() + ballot.cast = True + ballot.selection = selection + ballot.json_str = ballot_str + ballot.save() - voter = email_key[0].user - combine_encrypted_votes.delay(voter, poll) - email_voting_success.delay(voter, handle_json, event.title) + voter = email_key[0].user + combine_encrypted_votes.delay(voter, poll) + email_voting_success.delay(voter, handle_json, event.title) - if next_poll_uuid: - return HttpResponseRedirect(reverse('polls:event-vote', kwargs={'event_id': event.uuid, - 'poll_id': next_poll_uuid}) - + "?key=" + email_key_str) + if next_poll_uuid: + return HttpResponseRedirect(reverse('polls:event-vote', kwargs={'event_id': event.uuid, + 'poll_id': next_poll_uuid}) + + "?key=" + email_key_str) - return HttpResponse('Voted Successfully!') + return HttpResponse('Voted Successfully!') - return render(request, "polls/event_vote.html", - { - "object": poll, "poll_num": poll_num, "event": event, "poll_count": event.polls.all().count(), - "prev_uuid": prev_poll_uuid, "next_uuid": next_poll_uuid, "min_selection": poll.min_num_selections, - "max_selection": poll.max_num_selections, "can_vote": can_vote, "cant_vote_reason": cant_vote_reason, - "voter_email": voter_email, "has_voted": has_voted, "a_key": email_key_str - }) + return render(request, "polls/event_vote.html", + { + "object": poll, "poll_num": poll_num, "event": event, "poll_count": event.polls.all().count(), + "prev_uuid": prev_poll_uuid, "next_uuid": next_poll_uuid, "min_selection": poll.min_num_selections, + "max_selection": poll.max_num_selections, "can_vote": can_vote, "cant_vote_reason": cant_vote_reason, + "voter_email": voter_email, "has_voted": has_voted, "a_key": email_key_str + }) def event_trustee_setup(request, event_id): - # Obtain the event and the event preparation access key that's been supplied - event = get_object_or_404(Event, pk=event_id) - access_key = request.GET.get('key', None) + # Obtain the event and the event preparation access key that's been supplied + event = get_object_or_404(Event, pk=event_id) + access_key = request.GET.get('key', None) - # If the a_key is present, check it's valid and related to a trustee EmailUser instance for this event - if access_key: - 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(): - 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 the a_key is present, check it's valid and related to a trustee EmailUser instance for this event + if access_key: + 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(): + 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.get("public_key") - key = TrusteeKey.objects.get_or_create(event=event, user=email_key[0].user)[0] - key.key = public_key - key.save() + # If form data is valid, create a TrusteeKey object with the supplied public key + if form.is_valid(): + 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() - # When all trustees have supplied their public key, we can combine them to create a master key - # The event will now be ready to receive votes on the various polls that have been defined - - # voters therefore need to be informed - if event.trustee_keys.count() == event.users_trustees.count(): - create_ballots.delay(event) - generate_combpk.delay(event) - email_voters_vote_url.delay(event.voters.all(), event) - email_organisers_next_steps.delay(event) + # When all trustees have supplied their public key, we can combine them to create a master key + # The event will now be ready to receive votes on the various polls that have been defined - + # voters therefore need to be informed + if event.trustee_keys.count() == event.users_trustees.count(): + 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) + success_msg = 'You have successfully submitted your public key for this event!' + messages.add_message(request, messages.SUCCESS, success_msg) - 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, - "is_trustee": True, - "can_submit": True - }) + 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, + "is_trustee": True, + "can_submit": True + }) - 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." - }) + 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): - event = get_object_or_404(Event, pk=event_id) + event = get_object_or_404(Event, pk=event_id) - if not event.ended: - event_ended.delay(event) + if not event.ended: + event_ended.delay(event) - # Mark the event as ended - event.ended = True - event.save() + # Mark the event as ended + event.ended = True + event.save() - return HttpResponseRedirect(reverse('polls:view-event', args=[event_id])) + return HttpResponseRedirect(reverse('polls:view-event', args=[event_id])) def event_trustee_decrypt(request, event_id): - event = get_object_or_404(Event, pk=event_id) - access_key = request.GET.get('key', None) + event = get_object_or_404(Event, pk=event_id) + access_key = request.GET.get('key', None) - if access_key: - email_key = event.keys.filter(key=access_key) - trustee = email_key[0].user + if access_key: + email_key = event.keys.filter(key=access_key) + trustee = email_key[0].user - if email_key.exists() and event.users_trustees.filter(email=trustee.email).exists(): + 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(): - 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 + if PartialBallotDecryption.objects.filter(event=event, user=trustee).count() == event.total_num_opts(): + 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 - # Gen a list of ciphers from the combined ballots for every opt of every poll - polls = event.polls.all() - poll_ciphers = [] + # Gen a list of ciphers from the combined ballots for every opt of every poll + polls = event.polls.all() + poll_ciphers = [] - for poll in polls: - options = poll.options.all() + for poll in polls: + options = poll.options.all() - options_ciphers = [] - for option in options: - combined_ballot = CombinedBallot.objects.filter(poll=poll, option=option).get() + options_ciphers = [] + for option in options: + combined_ballot = CombinedBallot.objects.filter(poll=poll, option=option).get() - cipher = {} - cipher['C1'] = combined_ballot.cipher_text_c1 - cipher['C2'] = combined_ballot.cipher_text_c2 - options_ciphers.append(cipher) + cipher = {} + cipher['C1'] = combined_ballot.cipher_text_c1 + cipher['C2'] = combined_ballot.cipher_text_c2 + options_ciphers.append(cipher) - poll_ciphers.append(options_ciphers) + poll_ciphers.append(options_ciphers) - return render(request, - "polls/event_decrypt.html", - { - "event": event, - "user_email": trustee.email, - "trustee_pk": trustee_pk, - "poll_ciphers": poll_ciphers, - "is_trustee": True, - "can_submit": True - }) + return render(request, + "polls/event_decrypt.html", + { + "event": event, + "user_email": trustee.email, + "trustee_pk": trustee_pk, + "poll_ciphers": poll_ciphers, + "is_trustee": True, + "can_submit": True + }) - elif request.method == "POST": - polls = event.polls.all() - polls_count = len(polls) + elif request.method == "POST": + polls = event.polls.all() + polls_count = len(polls) - for i in range(polls_count): - options = polls[i].options.all() - options_count = len(options) + for i in range(polls_count): + options = polls[i].options.all() + options_count = len(options) - for j in range(options_count): - input_name = str("") - input_name = "poll-" + str(i) + "-cipher-" + str(j) + for j in range(options_count): + input_name = str("") + input_name = "poll-" + str(i) + "-cipher-" + str(j) - part_dec = request.POST.get(input_name) + part_dec = request.POST.get(input_name) - PartialBallotDecryption.objects.create(event=event, - poll=polls[i], - option=options[j], - user=trustee, - text=part_dec) + PartialBallotDecryption.objects.create(event=event, + poll=polls[i], + option=options[j], + user=trustee, + text=part_dec) - if event.all_part_decs_received(): - # 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("") + if event.all_part_decs_received(): + # 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")) + messages.add_message(request, messages.SUCCESS, 'Your partial decryptions have been successfully submitted') + return HttpResponseRedirect(reverse("user_home")) - # Without an access key, the client does not have permission to access this page - 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." - }) + # Without an access key, the client does not have permission to access this page + 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): - event = get_object_or_404(Event, pk=event_id) + event = get_object_or_404(Event, pk=event_id) - if (request.user.is_anonymous()) or (not event.users_organisers.filter(email=request.user.email).exists()): - messages.add_message(request, messages.WARNING, 'You do not have permission to access: ' + request.path) - return HttpResponseRedirect(reverse("user_home")) + if (request.user.is_anonymous()) or (not event.users_organisers.filter(email=request.user.email).exists()): + messages.add_message(request, messages.WARNING, 'You do not have permission to access: ' + request.path) + return HttpResponseRedirect(reverse("user_home")) - poll = Poll() - formset = OptionFormset(instance=poll, prefix="formset_organiser") + poll = Poll() + formset = OptionFormset(instance=poll, prefix="formset_organiser") - if request.method == "POST": - form = PollForm(request.POST, prefix="main") - formset = OptionFormset(request.POST, prefix="formset_organiser") # incase form fails, we still want to retain formset data - if form.is_valid(): - poll = form.save(commit=False) - poll.event_id = event_id - poll.save() - formset = OptionFormset(request.POST, prefix="formset_organiser", instance=poll) - if formset.is_valid(): - formset.save() - create_ballots_for_poll.delay(poll) - messages.add_message(request, messages.SUCCESS, 'Poll created successfully') - return HttpResponseRedirect(reverse('polls:event-polls', args=[poll.event_id])) + if request.method == "POST": + form = PollForm(request.POST, prefix="main") + formset = OptionFormset(request.POST, prefix="formset_organiser") # incase form fails, we still want to retain formset data + if form.is_valid(): + poll = form.save(commit=False) + poll.event_id = event_id + poll.save() + formset = OptionFormset(request.POST, prefix="formset_organiser", instance=poll) + if formset.is_valid(): + formset.save() + create_ballots_for_poll.delay(poll) + messages.add_message(request, messages.SUCCESS, 'Poll created successfully') + return HttpResponseRedirect(reverse('polls:event-polls', args=[poll.event_id])) - return render(request, "polls/create_poll.html", {"event": event, "question_form": form, "option_formset": formset}) + return render(request, "polls/create_poll.html", {"event": event, "question_form": form, "option_formset": formset}) - elif request.method == "GET": - form = PollForm(prefix="main") #, instance=poll - return render(request, "polls/create_poll.html", {"event": event, "question_form": form, "option_formset": formset}) - else: - return HttpResponseNotAllowed() + elif request.method == "GET": + form = PollForm(prefix="main") #, instance=poll + return render(request, "polls/create_poll.html", {"event": event, "question_form": form, "option_formset": formset}) + else: + return HttpResponseNotAllowed() def render_invalid(request, events, demo_users, invalid_fields): - return render(request, - "polls/create_event.html", - { - "G_R_SITE_KEY": settings.RECAPTCHA_PUBLIC_KEY, - "user_email": request.user.email, - "events": events, - "demo_users": demo_users, - "invalid_fields": invalid_fields - }) + return render(request, + "polls/create_event.html", + { + "G_R_SITE_KEY": settings.RECAPTCHA_PUBLIC_KEY, + "user_email": request.user.email, + "events": events, + "demo_users": demo_users, + "invalid_fields": invalid_fields + }) def create_event(request): - # Obtain context data for the rendering of the html template and validation - events = Event.objects.all() - demo_users = DemoUser.objects.all() + # Obtain context data for the rendering of the html template and validation + events = Event.objects.all() + demo_users = DemoUser.objects.all() - if request.method == "POST": - '''Perform Google reCAPTCHA validation''' - recaptcha_response = request.POST.get('g-recaptcha-response') - url = 'https://www.google.com/recaptcha/api/siteverify' - values = { - 'secret': settings.RECAPTCHA_PRIVATE_KEY, - 'response': recaptcha_response - } - data = urllib.urlencode(values) - req = urllib2.Request(url, data) - response = urllib2.urlopen(req) - result = json.load(response) + if request.method == "POST": + '''Perform Google reCAPTCHA validation''' + recaptcha_response = request.POST.get('g-recaptcha-response') + url = 'https://www.google.com/recaptcha/api/siteverify' + values = { + 'secret': settings.RECAPTCHA_PRIVATE_KEY, + 'response': recaptcha_response + } + data = urllib.urlencode(values) + req = urllib2.Request(url, data) + response = urllib2.urlopen(req) + result = json.load(response) - '''Perform form data validation''' - adaptor = EventModelAdaptor(request.POST, request.user) - form_data_valid = adaptor.isFormDataValid(events, demo_users) + '''Perform form data validation''' + adaptor = EventModelAdaptor(request.POST, request.user) + form_data_valid = adaptor.isFormDataValid(events, demo_users) - '''Process form data based on above results''' - if result['success']: - if form_data_valid: - # Create the new event using the form data - adaptor.extractData() - new_event = adaptor.updateModel() + '''Process form data based on above results''' + if result['success']: + if form_data_valid: + # Create the new event using the form data + adaptor.extractData() + new_event = adaptor.updateModel() - # Update the EID to include the GP in its EID - update_EID.delay(new_event) + # Update the EID to include the GP in its EID + update_EID.delay(new_event) - # Send an email to all trustees for event preparation - trustees = new_event.users_trustees.all() - email_trustees_prep.delay(trustees, new_event) + # Send an email to all trustees for event preparation + trustees = new_event.users_trustees.all() + email_trustees_prep.delay(trustees, new_event) - adaptor.clear_data() + adaptor.clear_data() - return HttpResponseRedirect(reverse('polls:index')) - else: - invalid_fields = adaptor.getInvalidFormFields() - adaptor.clear_data() - return render_invalid(request, events, demo_users, invalid_fields) - else: - invalid_fields = adaptor.getInvalidFormFields() - invalid_fields['recaptcha'] = {'error': 'The reCAPTCHA server validation failed, please try again.'} - adaptor.clear_data() - return render_invalid(request, events, demo_users, invalid_fields) + return HttpResponseRedirect(reverse('polls:index')) + else: + invalid_fields = adaptor.getInvalidFormFields() + adaptor.clear_data() + return render_invalid(request, events, demo_users, invalid_fields) + else: + invalid_fields = adaptor.getInvalidFormFields() + invalid_fields['recaptcha'] = {'error': 'The reCAPTCHA server validation failed, please try again.'} + adaptor.clear_data() + return render_invalid(request, events, demo_users, invalid_fields) - elif request.method == "GET": - # Render the template - return render(request, - "polls/create_event.html", - { - "G_R_SITE_KEY": settings.RECAPTCHA_PUBLIC_KEY, - "user_email": request.user.email, - "events": events, - "demo_users": demo_users - }) - else: - return HttpResponseNotAllowed() + elif request.method == "GET": + # Render the template + return render(request, + "polls/create_event.html", + { + "G_R_SITE_KEY": settings.RECAPTCHA_PUBLIC_KEY, + "user_email": request.user.email, + "events": events, + "demo_users": demo_users + }) + else: + return HttpResponseNotAllowed() def edit_event(request, event_id): - event = get_object_or_404(Event, pk=event_id) - if request.method == "GET": - form = EventEditForm(instance=event, prefix="main") - ''' - organiser_initial_data = [{'email': request.user.email}] - trustee_initial_data = [] - for user in event.users_organisers.exclude(email=request.user.email): - organiser_initial_data.append({'email': user.email}) - organiser_formset = OrganiserFormSet(prefix="formset_organiser", initial=organiser_initial_data) - for trustee in event.users_trustees.all(): - trustee_initial_data.append({'email': trustee.email}) - trustee_formset = TrusteeFormSet(prefix="formset_trustee", initial=trustee_initial_data) - ''' - elif request.method == "POST": - form = EventEditForm(request.POST, instance=event, prefix="main") - #trustee_formset = TrusteeFormSet(request.POST, prefix="formset_trustee") - #organiser_formset = OrganiserFormSet(request.POST, prefix="formset_organiser") # incase form fails, we still want to retain formset data - if form.is_valid(): - form.save() - ''' - if organiser_formset.is_valid(): - event.users_organisers.clear() - for oform in organiser_formset: - if (oform.cleaned_data.get('email')): - event.users_organisers.add(DemoUser.objects.get(email=oform.cleaned_data['email'])) - event.users_organisers.add(request.user) # always add editor/creator - if trustee_formset.is_valid(): - event.users_trustees.clear() - for tform in trustee_formset: - if (tform.cleaned_data.get('email')): - event.users_trustees.add(EmailUser.objects.get_or_create(email=tform.cleaned_data['email'])[0]) - ''' - return HttpResponseRedirect(reverse('polls:view-event', kwargs={'pk': event.id})) - return render(request, "polls/generic_form.html", {"form_title": "Edit Event: " + event.title, "form": form}) #"organiser_formset": organiser_formset, "trustee_formset": trustee_formset}) - #trustee_formset = TrusteeFormSet(request.POST, prefix="formset_trustee", instance=event) + event = get_object_or_404(Event, pk=event_id) + if request.method == "GET": + form = EventEditForm(instance=event, prefix="main") + ''' + organiser_initial_data = [{'email': request.user.email}] + trustee_initial_data = [] + for user in event.users_organisers.exclude(email=request.user.email): + organiser_initial_data.append({'email': user.email}) + organiser_formset = OrganiserFormSet(prefix="formset_organiser", initial=organiser_initial_data) + for trustee in event.users_trustees.all(): + trustee_initial_data.append({'email': trustee.email}) + trustee_formset = TrusteeFormSet(prefix="formset_trustee", initial=trustee_initial_data) + ''' + elif request.method == "POST": + form = EventEditForm(request.POST, instance=event, prefix="main") + #trustee_formset = TrusteeFormSet(request.POST, prefix="formset_trustee") + #organiser_formset = OrganiserFormSet(request.POST, prefix="formset_organiser") # incase form fails, we still want to retain formset data + if form.is_valid(): + form.save() + ''' + if organiser_formset.is_valid(): + event.users_organisers.clear() + for oform in organiser_formset: + if (oform.cleaned_data.get('email')): + event.users_organisers.add(DemoUser.objects.get(email=oform.cleaned_data['email'])) + event.users_organisers.add(request.user) # always add editor/creator + if trustee_formset.is_valid(): + event.users_trustees.clear() + for tform in trustee_formset: + if (tform.cleaned_data.get('email')): + event.users_trustees.add(EmailUser.objects.get_or_create(email=tform.cleaned_data['email'])[0]) + ''' + return HttpResponseRedirect(reverse('polls:view-event', kwargs={'pk': event.id})) + return render(request, "polls/generic_form.html", {"form_title": "Edit Event: " + event.title, "form": form}) #"organiser_formset": organiser_formset, "trustee_formset": trustee_formset}) + #trustee_formset = TrusteeFormSet(request.POST, prefix="formset_trustee", instance=event) def del_event(request, event_id): - event = get_object_or_404(Event, pk=event_id) - if request.method == "GET": - return render(request, "polls/del_event.html", {"event_title": event.title, "event_id": event.uuid}) - elif request.method == "POST": - event.delete() - return HttpResponseRedirect(reverse('polls:index')) \ No newline at end of file + event = get_object_or_404(Event, pk=event_id) + if request.method == "GET": + return render(request, "polls/del_event.html", {"event_title": event.title, "event_id": event.uuid}) + elif request.method == "POST": + event.delete() + return HttpResponseRedirect(reverse('polls:index')) \ No newline at end of file diff --git a/allauthdemo/templates/bases/bootstrap-jquery.html b/allauthdemo/templates/bases/bootstrap-jquery.html index a4679ac..c49996a 100755 --- a/allauthdemo/templates/bases/bootstrap-jquery.html +++ b/allauthdemo/templates/bases/bootstrap-jquery.html @@ -21,6 +21,7 @@ + diff --git a/allauthdemo/templates/polls/find_ballot.html b/allauthdemo/templates/polls/find_ballot.html new file mode 100644 index 0000000..23ac490 --- /dev/null +++ b/allauthdemo/templates/polls/find_ballot.html @@ -0,0 +1,11 @@ +{% extends "bases/bootstrap-with-nav.html" %} +{% load staticfiles %} +{% load bootstrap3 %} + +{% block content %} + + + +

+ +{% endblock %} \ No newline at end of file diff --git a/allauthdemo/templates/polls/vote_audit.html b/allauthdemo/templates/polls/vote_audit.html index c587f84..a13ae55 100644 --- a/allauthdemo/templates/polls/vote_audit.html +++ b/allauthdemo/templates/polls/vote_audit.html @@ -4,15 +4,31 @@ {% block content %} - - -
-
- -
{{ ballot }}
- -

-
-

+
+
+
+
+
+
+
+ + +
{{ ballot }}
+ + + +

+
+

+
+
+ +
{{ ballot2 }}
+ + + +

+
+

 
 {% endblock %}
\ No newline at end of file
diff --git a/static/js/event_vote.js b/static/js/event_vote.js
index 44fb60c..1945560 100644
--- a/static/js/event_vote.js
+++ b/static/js/event_vote.js
@@ -328,12 +328,51 @@ function generateBallots() {
         var ballotB = generateBallot();
         progressBar.setAttribute("style", "width: 100%;");
 
-        showFirstQRCode(ballotA, ballotB, selectedOption);
+        showIDsQRCode(ballotA, ballotB, selectedOption);
     }, 150);
 }
 
+function showIDsQRCode(ballotA, ballotB, selectedOption) {
+    var voterID = window.location.search.slice(1).split(/=(.+)/)[1];
+    var eventID = window.location.href.split('/')[4];
+    var pollID = $('#poll-num').text();
+
+    // Display a QR code with ID details
+    var modalDialog = $('#modalDialog');
+    var title = modalDialog.find('.modal-title');
+    var body = modalDialog.find('.modal-body');
+
+    body.empty();
+    title.text('Step 0 of 3: Scan this');
+
+    let pleaseScanP = document.createElement('p');
+    pleaseScanP.innerHTML = "Please scan the following QR code from your DEMOS 2 mobile application:";
+
+    let QRDiv = document.createElement('div');
+    var QRCodeImg = document.createElement('img');
+    QRCodeImg.setAttribute('class', 'QR-code');
+    QRCodeImg.setAttribute('id', "qr-img");
+    new QRCode(QRCodeImg, voterID+";"+eventID+";"+pollID);
+    QRDiv.append(QRCodeImg);
+    
+    body.append(pleaseScanP);
+    body.append(QRDiv);
+    
+     // Prepare the appropriate dialog buttons
+    updateDialogButtons(DIALOG_BTN_STATES.STEP_1);
+
+    if(!dialogOpen) {
+        modalDialog.modal('toggle');
+        dialogOpen = true;
+    }
+
+    $('#nextDialogBtn').click(function(e) {
+        showHashQRCode(ballotA, ballotB, selectedOption);
+    });
+};
+
 // Called in stage 1 of 3 in the voting process
-function showFirstQRCode(ballotA, ballotB, selectedOption) {
+function showHashQRCode(ballotA, ballotB, selectedOption) {
     var ballots = new Array(ballotA, ballotB);
     var ballotHashes = new Array(2);
 
@@ -350,7 +389,7 @@ function showFirstQRCode(ballotA, ballotB, selectedOption) {
     title.text('Step 1 of 3: Link Your Vote');
 
     let pleaseScanP = document.createElement('p');
-    pleaseScanP.innerHTML = "Please scan the following QR code from your DEMOS 2 mobile application:";
+    pleaseScanP.innerHTML = "Please scan the following QR code from your DEMOS 2 auditor:";
 
     let QRDiv = document.createElement('div');
     var QRCodeImg = document.createElement('img');
@@ -551,19 +590,22 @@ function sendBallotsToServer(selection, selectedBallot, otherBallot) {
     var pk = new ctx.ECP(0);
     pk.copy(tempPK.PK);
 
-    var voterID = window.location.search.slice(1).split(/=(.+)/)[1];//.slice(0, -2);
+    // Prepares the unselected ballot to send, encrypted, to the server.
+    // Creates the unique ballot ID for the unselected ballot
+    var voterID = window.location.search.slice(1).split(/=(.+)/)[1];
     var eventID = window.location.href.split('/')[4];
     var pollNum = $('#poll-num').text();
     var ballotID = encodeURIComponent(btoa(JSON.stringify({voterID: voterID, eventID: eventID, pollNum: pollNum})));
-
     // TODO: Generate a SK rather than using a static one. UUID generated server side and then injected JS side?
-    JSON.stringify(otherBallot)
     var SK = "temporary";
-    var encAlt = sjcl.encrypt(SK, JSON.stringify(otherBallot));
+    var encAlt = sjcl.encrypt(SK, JSON.stringify({ballotID: ballotID, ballot: otherBallot}));
+    
+    // Generates an HMAC to protect the encrypted ballot from tampering.
     var out = (new sjcl.misc.hmac(key, sjcl.hash.sha256)).mac(encAlt);
     var hmac = sjcl.codec.hex.fromBits(out);
+    
+    // Prepares the selected ballot to submit to the server.
     let selectedBallotAsStr = JSON.stringify(selectedBallot);
-
     $.ajax({
          type : "POST",
          url : window.location,
@@ -593,7 +635,7 @@ function onAfterBallotSend(ballotID, SK, hmac) {
     instructions1P.innerHTML = instructions1Txt;
     body.append(instructions1P);
 
-    // Add the second section: QR code that contains the ballot identifier
+    // Add the second section: QR code that contains the ballot identifier and HMAC
     var QRCodeImg = document.createElement('img');
     QRCodeImg.setAttribute('class', 'QR-code');
     new QRCode(QRCodeImg, ballotID+";"+btoa(hmac));
diff --git a/static/js/find_ballot.js b/static/js/find_ballot.js
new file mode 100644
index 0000000..b3e1059
--- /dev/null
+++ b/static/js/find_ballot.js
@@ -0,0 +1,10 @@
+$( document ).ready(function() {
+   ballotHash = SHA256Hash(stringtobytes(JSON.stringify($('#ballot-found').text())), true);
+   var hashes = $('#ballot_hashes').text().split(';');
+   $('#ballot_result').text("Ballot not found!");
+   for (var i = 0; i <= 1; i++) {
+      if (hashes[i] == ballotHash) {
+         $('#ballot_result').text("Ballot found!");
+      }
+   }
+});
\ No newline at end of file
diff --git a/static/js/vote_audit.js b/static/js/vote_audit.js
index 9bf133e..0fb3b02 100644
--- a/static/js/vote_audit.js
+++ b/static/js/vote_audit.js
@@ -6,6 +6,25 @@ function getBytes(arr) {
     return new Uint8Array(arr);
 }
 
+$( document ).ready(function() {
+   $('.gp-2, .gp-3').hide();
+   var n = 0;
+   for (var i = 0; i <= 1; i++) {
+      if ($('#handle'+i).val() != "") {
+         n++;
+      }
+   }
+   if (n == 2) {
+      $('ballot-group'+i+' .gp-1').css('opacity','0.6');
+      $('#retrieve-ballots').prop('disabled', true);
+      $('ballot-group'+i+' .gp-2').show();
+   }
+});
+
+$('#retrieve-ballots').click(function() {
+   window.location = "/audit?handle="+$('#handle1').val()+'&handle2='+$('#handle2').val();
+});
+
 $('#begin-test').click(function() {
     var ctx = new CTX("BN254CX");
     var ciphertext = {
@@ -44,7 +63,19 @@ $('#begin-test').click(function() {
             // test whether C2/(C1)^r = g^0 or g^1, and record g's exponent.
             //var c1 = ctx.PAIR.GTpow(ciphertext.C1, ciphertext.r);
 
-            var m = ciphertext.C2.div(Math.pow(ciphertext.C1, ciphertext.r));
+            var B;
+            var j;
+            for (j = 0; j <= 1; j++) {
+                //use D as temp var
+                B = new ctx.BIG(j);
+                D = ctx.PAIR.G1mul(params.g1,B);
+                if (D.equals(gM)) {
+                    return {
+                        M:j
+                    }
+                }
+            };
+
             console.log("m = "+m);
             encoding += (m) ? "1" : "0";