Full end-to-end voting is working using the new binary encoding scheme, ballot combination and trustee partial decryption with tallies working perfectly. This required updating the Node server as well as Django models and views to support this. Emails to voters and trustees have also been updated to be more informative and look more professional. It could probably do at this point with using email templates and in the future HTML emails.
This commit is contained in:
parent
571cd723bc
commit
5b746ad406
14 changed files with 631 additions and 555 deletions
|
@ -1,6 +1,7 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from django.core.mail import send_mail
|
||||
from django.db import models
|
||||
|
@ -8,6 +9,7 @@ from django.utils import timezone
|
|||
|
||||
from allauthdemo.auth.models import DemoUser
|
||||
|
||||
|
||||
class EmailUser(models.Model):
|
||||
email = models.CharField(max_length=80, unique=True)
|
||||
|
||||
|
@ -35,16 +37,23 @@ class Event(models.Model):
|
|||
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):
|
||||
EID_json = json.loads(self.EID)
|
||||
return EID_json['hr']
|
||||
try:
|
||||
EID_json = json.loads(self.EID)
|
||||
return EID_json['hr']
|
||||
except ValueError:
|
||||
return self.EID
|
||||
|
||||
def EID_crypto(self):
|
||||
EID_json = json.loads(self.EID)
|
||||
EID_crypto_str = EID_json['crypto']
|
||||
return json.loads(EID_crypto_str)
|
||||
try:
|
||||
EID_json = json.loads(self.EID)
|
||||
EID_crypto_str = EID_json['crypto']
|
||||
return json.loads(EID_crypto_str)
|
||||
except ValueError:
|
||||
return "None - Event not Initialised"
|
||||
|
||||
def duration(self):
|
||||
duration_str = self.start_time_formatted()
|
||||
|
@ -63,26 +72,55 @@ class Event(models.Model):
|
|||
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
|
||||
|
||||
for poll in polls:
|
||||
count += poll.options.all().count()
|
||||
|
||||
return count
|
||||
|
||||
def total_num_partial_decs(self):
|
||||
polls = self.polls.all()
|
||||
count = 0
|
||||
|
||||
for poll in polls:
|
||||
count += PartialBallotDecryption.objects.filter(poll=poll).count()
|
||||
|
||||
return count
|
||||
|
||||
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
|
||||
|
||||
return received
|
||||
|
||||
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
|
||||
# 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.public_key is None:
|
||||
if present < self.start_time and self.prepared is False:
|
||||
status_str = "Future"
|
||||
elif present < self.start_time and self.public_key is not None:
|
||||
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.public_key is not None:
|
||||
elif present >= self.start_time and present <= self.end_time and self.prepared is True:
|
||||
status_str = "Active"
|
||||
elif present > self.end_time and self.public_key is not None:
|
||||
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.event_sk.all().count() == 1:
|
||||
if self.all_part_decs_received():
|
||||
status_str = "Decrypted"
|
||||
elif self.event_sk.all().count() == 0:
|
||||
else:
|
||||
status_str = "Ended"
|
||||
|
||||
return status_str
|
||||
|
@ -109,13 +147,12 @@ class TrusteeKey(models.Model):
|
|||
user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="trustee_keys")
|
||||
key = models.CharField(max_length=255, unique=True)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
#total = models.IntegerField(blank=True, null=True, default=0)
|
||||
|
||||
def has_started(self):
|
||||
return timezone.now() >= self.start
|
||||
|
||||
|
@ -125,60 +162,60 @@ class AccessKey(models.Model):
|
|||
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")
|
||||
enc = models.CharField(max_length=4096, null=True)
|
||||
|
||||
#index = models.IntegerField()
|
||||
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
|
||||
|
||||
|
||||
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")
|
||||
#index = models.IntegerField()
|
||||
|
||||
def __str__(self):
|
||||
return self.choice_text
|
||||
|
||||
class Decryption(models.Model):
|
||||
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="decryptions")
|
||||
poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="decryptions")
|
||||
user = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="decryptions")
|
||||
text = models.CharField(max_length=1024)
|
||||
|
||||
class TrusteeSK(models.Model):
|
||||
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="trustee_sk")
|
||||
trustee = models.ForeignKey(EmailUser, on_delete=models.CASCADE, related_name="trustee_sk")
|
||||
key = models.CharField(max_length=1024)
|
||||
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)
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
class EventSK(models.Model):
|
||||
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="event_sk")
|
||||
key = models.CharField(max_length=1024)
|
||||
|
||||
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")
|
||||
cast = models.BooleanField(default=False)
|
||||
|
||||
|
||||
# Implements the new binary encoding scheme
|
||||
class EncryptedVote(models.Model):
|
||||
ballot = models.ForeignKey(Ballot, on_delete=models.CASCADE, related_name="encrypted_vote")
|
||||
|
||||
|
||||
class VoteFragment(models.Model):
|
||||
encrypted_vote = models.ForeignKey(EncryptedVote, on_delete=models.CASCADE, related_name="fragment")
|
||||
cipher_text_c1 = models.CharField(max_length=4096)
|
||||
cipher_text_c2 = models.CharField(max_length=4096)
|
||||
|
||||
class Organiser(models.Model):
|
||||
index = models.IntegerField(default=0)
|
||||
email = models.CharField(max_length=100, blank=False, null=False)
|
||||
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||||
|
||||
|
||||
|
|
Reference in a new issue