Implemented full end-to-end encryption and decryption of an event using trustee public and secret keys. This required modification of the NodeJS crypto server to receive post data from the crypto_rpc py methods and for combining SKs together. Additionally, the new binary voting encoding scheme has been implemented for encryption and decryption of the event. General UI improvements have been made and as well as some other bug fixes
This commit is contained in:
parent
0c354cd542
commit
e33b91f852
23 changed files with 809 additions and 446 deletions
|
@ -1,6 +1,3 @@
|
|||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import json
|
||||
import urllib2
|
||||
|
||||
|
@ -10,26 +7,40 @@ All functions in this file have been re-implemenented by Thomas Smith
|
|||
|
||||
File then updated by Vincent de Almeida. Changes include:
|
||||
-Update filename to 'crypto_rpc' to reflect the RPC nature of the methods
|
||||
-Modified RPC calls that send data to POST requests to avoid large query URLs
|
||||
|
||||
'''
|
||||
|
||||
|
||||
def send_post_req(url, data):
|
||||
data = json.dumps(data)
|
||||
|
||||
# Create a request specifying the Content-Type
|
||||
req = urllib2.Request(url, data, {'Content-Type': 'application/json'})
|
||||
f = urllib2.urlopen(req)
|
||||
response = f.read()
|
||||
f.close()
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def param():
|
||||
url = 'http://localhost:8080/param' # RPC URL
|
||||
url = 'http://localhost:8080/param'
|
||||
jsondict = json.load(urllib2.urlopen(url))
|
||||
return json.dumps(jsondict)
|
||||
|
||||
def combpk(amount, pks):
|
||||
url = 'http://localhost:8080/cmpkstring' # RPC URL
|
||||
querystring = '?number='+str(amount)
|
||||
for pk in pks:
|
||||
querystring += '&PK='+pk
|
||||
|
||||
print(url+querystring)
|
||||
jsondict = json.load(urllib2.urlopen(url+querystring))
|
||||
print(json.dumps(jsondict))
|
||||
return json.dumps(jsondict)
|
||||
def combpk(pks):
|
||||
url = 'http://localhost:8080/cmpkstring'
|
||||
|
||||
data = {}
|
||||
data['PKs'] = pks
|
||||
|
||||
return send_post_req(url, data)
|
||||
|
||||
|
||||
def addec(amount, ciphers):
|
||||
url = 'http://localhost:8080/addec' # RPC URL
|
||||
url = 'http://localhost:8080/addec'
|
||||
querystring = '?number='+str(amount)
|
||||
c1s = ciphers['c1s']
|
||||
c2s = ciphers['c2s']
|
||||
|
@ -42,23 +53,44 @@ def addec(amount, ciphers):
|
|||
print(json.dumps(jsondict))
|
||||
return json.dumps(jsondict)
|
||||
|
||||
def tally(amount, param, decs, cipher):
|
||||
url = 'http://localhost:8080/tally' # RPC URL
|
||||
querystring = '?number='+str(amount)
|
||||
querystring += '¶m='+urllib2.quote(str(param))
|
||||
|
||||
testquerystring = '?number='+str(amount)
|
||||
testquerystring += '¶m='+str(param)
|
||||
# Deprecated functionality and has been superseded by get_tally
|
||||
def tally(amount, group_param, decs, cipher):
|
||||
url = 'http://localhost:8080/tally'
|
||||
querystring = '?number='+str(amount)
|
||||
querystring += '¶m='+urllib2.quote(str(group_param))
|
||||
|
||||
for i, value in enumerate(decs):
|
||||
querystring += "&decs="+str(value)
|
||||
testquerystring += "&decs="+str(value)
|
||||
|
||||
querystring += '&cipher=' + urllib2.quote(str(cipher))
|
||||
testquerystring += '&cipher=' + str(cipher)
|
||||
|
||||
print(url+querystring)
|
||||
print(url+testquerystring)
|
||||
jsondict = json.load(urllib2.urlopen(url+querystring))
|
||||
print('tally: ' + str(jsondict['M']))
|
||||
return str(jsondict['M'])
|
||||
|
||||
return str(jsondict['M'])
|
||||
|
||||
|
||||
def combine_sks(sks):
|
||||
url = 'http://localhost:8080/comb_sks'
|
||||
|
||||
# Construct POST data
|
||||
data = {}
|
||||
data['SKs'] = sks
|
||||
|
||||
# Return the new combined SK
|
||||
return send_post_req(url, data)
|
||||
|
||||
|
||||
def get_tally(count, ciphers, sk, group_param):
|
||||
url = 'http://localhost:8080/get_tally'
|
||||
|
||||
# Construct POST data
|
||||
data = {}
|
||||
data['count'] = count
|
||||
data['ciphers'] = ciphers
|
||||
data['sk'] = sk
|
||||
data['param'] = group_param
|
||||
|
||||
# Return the tally of votes for the option
|
||||
return send_post_req(url, data)
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ class Event(models.Model):
|
|||
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)
|
||||
|
@ -35,6 +36,7 @@ class Event(models.Model):
|
|||
c_email = models.CharField(max_length=512, blank=True)
|
||||
trustees = models.CharField(max_length=4096)
|
||||
|
||||
# Custom helper methods
|
||||
def EID_hr(self):
|
||||
EID_json = json.loads(self.EID)
|
||||
return EID_json['hr']
|
||||
|
@ -68,15 +70,36 @@ class Event(models.Model):
|
|||
# future event
|
||||
present = timezone.now()
|
||||
|
||||
if present >= self.start_time and present <= self.end_time:
|
||||
status_str = "Active"
|
||||
elif present > self.end_time:
|
||||
status_str = "Expired"
|
||||
elif present < self.start_time:
|
||||
status_str = "Future"
|
||||
if self.ended is False:
|
||||
if present < self.start_time and self.public_key is None:
|
||||
status_str = "Future"
|
||||
elif present < self.start_time and self.public_key is not None:
|
||||
status_str = "Prepared"
|
||||
elif present >= self.start_time and present <= self.end_time and self.public_key is not None:
|
||||
status_str = "Active"
|
||||
elif present > self.end_time and self.public_key is not None:
|
||||
status_str = "Expired"
|
||||
else:
|
||||
if self.event_sk.all().count() == 1:
|
||||
status_str = "Decrypted"
|
||||
elif self.event_sk.all().count() == 0:
|
||||
status_str = "Ended"
|
||||
|
||||
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
|
||||
|
||||
for poll in self.polls.all():
|
||||
if Ballot.objects.filter(poll=poll, cast=True).count() == 0:
|
||||
received_votes = False
|
||||
|
||||
return received_votes
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
@ -84,12 +107,12 @@ class Event(models.Model):
|
|||
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=1024, unique=True) # ideally composite key here, but django doesn't really support yet
|
||||
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=1024, unique=True) # ideally composite key here, but django doesn't really support yet
|
||||
key = models.CharField(max_length=255, unique=True)
|
||||
|
||||
#total = models.IntegerField(blank=True, null=True, default=0)
|
||||
|
||||
|
@ -115,20 +138,6 @@ class Poll(models.Model):
|
|||
def __str__(self):
|
||||
return self.question_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)
|
||||
|
||||
#some modification to this class
|
||||
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")
|
||||
cipher_text_c1 = models.CharField(max_length=4096)#the encryption system uses two byte strings
|
||||
cipher_text_c2 = models.CharField(max_length=4096)
|
||||
cast = models.BooleanField(default=False)
|
||||
|
||||
class PollOption(models.Model):
|
||||
choice_text = models.CharField(max_length=200)
|
||||
votes = models.IntegerField(default=0)
|
||||
|
@ -138,6 +147,35 @@ class PollOption(models.Model):
|
|||
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 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)
|
||||
|
|
|
@ -5,14 +5,11 @@ import json
|
|||
from os import urandom
|
||||
from celery import task
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import EmailValidator
|
||||
from django.core.mail import send_mail
|
||||
from django.conf import settings
|
||||
|
||||
from allauthdemo.polls.models import AccessKey
|
||||
from allauthdemo.polls.models import AccessKey, Ballot, Decryption, TrusteeSK, EventSK
|
||||
|
||||
from .crypto_rpc import param, combpk, addec, tally
|
||||
from .crypto_rpc import param, combpk, addec, tally, get_tally, combine_sks
|
||||
|
||||
'''
|
||||
Goal: This py file defines celery tasks that can be initiated
|
||||
|
@ -25,25 +22,43 @@ from .crypto_rpc import param, combpk, addec, tally
|
|||
# Will store the result of the initial cal to param() from .cpp_calls
|
||||
group_param = None
|
||||
|
||||
def is_valid_email(email):
|
||||
try:
|
||||
valid_email = EmailValidator(whitelist=None)
|
||||
valid_email(email)
|
||||
return True
|
||||
except ValidationError:
|
||||
return False
|
||||
|
||||
@task()
|
||||
def create_ballots(poll):
|
||||
for voter in poll.event.voters.all():
|
||||
ballot = poll.ballots.create(voter=voter, poll=poll)
|
||||
|
||||
'''
|
||||
Will generate a key for accessing either the event preparation page or the voting page
|
||||
Helper functions
|
||||
|
||||
gen_access_key - Will generate an a key for accessing either the event preparation page, voting page and decryption page
|
||||
'''
|
||||
def gen_access_key():
|
||||
return base64.urlsafe_b64encode(urandom(16)).decode('utf-8')
|
||||
|
||||
def email_trustees_dec(event):
|
||||
email_subject = "Event Ballot Decryption for '" + event.title + "'"
|
||||
|
||||
# Plain text email - this could be replaced for a HTML-based email in the future
|
||||
email_body = "Please visit the following URL to submit your trustee secret key to begin event decryption:\n\n"
|
||||
url_base = "http://" + settings.DOMAIN + "/event/" + str(event.pk) + "/decrypt/?key="
|
||||
email_body = email_body + url_base
|
||||
|
||||
for trustee in event.users_trustees.all():
|
||||
# Generate a key and create an AccessKey object
|
||||
key = gen_access_key()
|
||||
AccessKey.objects.create(user=trustee, event=event, key=key)
|
||||
|
||||
trustee.send_email(email_subject, email_body + key)
|
||||
|
||||
@task()
|
||||
def create_ballots(event):
|
||||
voters = event.voters.all()
|
||||
|
||||
for poll in event.polls.all():
|
||||
for voter in voters:
|
||||
ballot = poll.ballots.create(voter=voter, poll=poll)
|
||||
|
||||
@task()
|
||||
def create_ballots_for_poll(poll):
|
||||
for voter in poll.event.voters.all():
|
||||
ballot = poll.ballots.create(voter=voter, poll=poll)
|
||||
|
||||
|
||||
'''
|
||||
Emails an event preparation URL containing an access key for all of the trustees for an event
|
||||
'''
|
||||
|
@ -64,19 +79,31 @@ def email_trustees_prep(trustees, event):
|
|||
trustee.send_email(email_subject, email_body + key)
|
||||
|
||||
'''
|
||||
Emails the access keys for all of the voters for an event
|
||||
Emails a URL containing an access key for all of the voters for an event
|
||||
'''
|
||||
@task()
|
||||
def email_voters_a_key(voters, event):
|
||||
def email_voters_vote_url(voters, event):
|
||||
email_subject = "Voting Access for Event '" + event.title + "'"
|
||||
email_body = 'Key: '
|
||||
|
||||
# Plain text email - this could be replaced for a HTML-based email in the future
|
||||
email_body_base = "Please visit the following URL in order to vote on the event '" + event.title + "':\n\n"
|
||||
url_base = "http://" + settings.DOMAIN + "/event/" + str(event.pk) + "/poll/1/vote/?key="
|
||||
email_body_base = email_body_base + url_base
|
||||
|
||||
duration_info = "\n\nYou can vote between the following dates and times:\n"
|
||||
duration_info = duration_info + "Start: " + event.start_time_formatted_utc() + "\n"
|
||||
duration_info = duration_info + "End: " + event.end_time_formatted_utc()
|
||||
|
||||
for voter in voters:
|
||||
# Generate a key and create an AccessKey object
|
||||
key = gen_access_key()
|
||||
AccessKey.objects.create(user=voter, event=event, key=key)
|
||||
|
||||
voter.send_email(email_subject, email_body + key)
|
||||
# Update the email body to incl the access key as well as the duration information
|
||||
email_body = str(email_body_base + key)
|
||||
email_body = email_body + duration_info
|
||||
|
||||
voter.send_email(email_subject, email_body)
|
||||
|
||||
'''
|
||||
Updates the EID of an event to contain 2 event IDs: a human readable one (hr) and a crypto one (GP from param())
|
||||
|
@ -93,6 +120,83 @@ def update_EID(event):
|
|||
event.EID = json.dumps(EID)
|
||||
event.save()
|
||||
|
||||
@task()
|
||||
def event_ended(event):
|
||||
# Email all trustees to request their secret keys
|
||||
email_trustees_dec(event)
|
||||
|
||||
@task()
|
||||
def gen_event_sk_and_dec(event):
|
||||
trustee_sks = TrusteeSK.objects.filter(event=event)
|
||||
t_sks_count = len(trustee_sks)
|
||||
|
||||
# Combine SKs if there's more than one
|
||||
event_sk = None
|
||||
if t_sks_count == 1:
|
||||
event_sk = trustee_sks.get().key
|
||||
else:
|
||||
t_sks_str_list = list()
|
||||
|
||||
for t_sk in trustee_sks:
|
||||
t_sks_str_list.append(t_sk.key)
|
||||
|
||||
event_sk = combine_sks(t_sks_str_list)
|
||||
|
||||
EventSK.objects.create(event=event, key=event_sk)
|
||||
|
||||
# With the event sk created, we can decrypt the event
|
||||
decrypt_and_tally(event)
|
||||
|
||||
@task()
|
||||
def decrypt_and_tally(event):
|
||||
polls = event.polls.all()
|
||||
sk = EventSK.objects.filter(event=event).get().key
|
||||
|
||||
for i in range(len(polls)):
|
||||
poll = polls[i]
|
||||
result = str("")
|
||||
result += "{\"name\": \"" + poll.question_text + "\","
|
||||
|
||||
# get num of opts and ballots
|
||||
options = poll.options.all()
|
||||
opt_count = len(options)
|
||||
ballots = Ballot.objects.filter(poll=poll, cast=True)
|
||||
|
||||
result += "\"options\": ["
|
||||
for j in range(opt_count):
|
||||
# Collect all fragments for this opt
|
||||
frags_c1 = list()
|
||||
frags_c2 = list()
|
||||
|
||||
for ballot in ballots:
|
||||
enc_vote = ballot.encrypted_vote.get()
|
||||
|
||||
if enc_vote is not None:
|
||||
fragments = enc_vote.fragment.all()
|
||||
frags_c1.append(fragments[j].cipher_text_c1)
|
||||
frags_c2.append(fragments[j].cipher_text_c2)
|
||||
|
||||
ciphers = {
|
||||
'c1s': frags_c1,
|
||||
'c2s': frags_c2
|
||||
}
|
||||
|
||||
count = len(frags_c1)
|
||||
votes = get_tally(count, ciphers, sk, event.EID)
|
||||
|
||||
result += "{\"option\": \"" + str(options[j].choice_text) + "\", \"votes\": " + str(votes) + "}"
|
||||
|
||||
if j != (opt_count-1):
|
||||
result += ","
|
||||
|
||||
result += "]}"
|
||||
|
||||
if i != (len(polls) - 1):
|
||||
result += ","
|
||||
|
||||
poll.enc = result
|
||||
poll.save()
|
||||
|
||||
@task()
|
||||
def tally_results(event):
|
||||
for poll in event.polls.all():
|
||||
|
@ -101,39 +205,42 @@ def tally_results(event):
|
|||
decs.append(dec.text)
|
||||
amount = len(decs)
|
||||
result = tally(amount, event.EID, decs, poll.enc)
|
||||
send_mail(
|
||||
'Your Results:',
|
||||
poll.question_text + ": " + result,
|
||||
'from@example.com',
|
||||
["fake@fake.com"],
|
||||
fail_silently=False,
|
||||
)
|
||||
|
||||
# TODO: Email organisers using email_user method?
|
||||
|
||||
print(poll.question_text + ": " + result)
|
||||
|
||||
@task()
|
||||
def generate_combpk(event):
|
||||
pks = list()
|
||||
|
||||
for tkey in event.trustee_keys.all():
|
||||
pks.append(str(tkey.key))
|
||||
amount = len(pks)
|
||||
event.public_key = combpk(amount, pks)
|
||||
|
||||
event.public_key = combpk(pks)
|
||||
|
||||
event.prepared = True
|
||||
event.save()
|
||||
|
||||
@task
|
||||
def generate_enc(poll):
|
||||
c1s = list()#c1 components of ciphertexts
|
||||
c2s = list()#c1 components of ciphertexts
|
||||
# c1 and c2 components of ciphertexts
|
||||
c1s = list()
|
||||
c2s = list()
|
||||
|
||||
for ballot in poll.ballots.all():
|
||||
if (ballot.cast):
|
||||
if ballot.cast:
|
||||
c1s.append(str(ballot.cipher_text_c1))
|
||||
c2s.append(str(ballot.cipher_text_c2))
|
||||
|
||||
ciphers = {
|
||||
'c1s':c1s,
|
||||
'c2s':c2s
|
||||
'c1s': c1s,
|
||||
'c2s': c2s
|
||||
}
|
||||
amount = len(c1s)
|
||||
poll.enc = addec(amount, ciphers)
|
||||
|
||||
count = len(c1s)
|
||||
|
||||
poll.enc = addec(count, ciphers)
|
||||
poll.save()
|
||||
|
||||
|
||||
|
|
|
@ -5,5 +5,17 @@ register = template.Library()
|
|||
#get a value for additively homomorphic encryption ballots
|
||||
#we can't do maths in the template normally so a filter is a way around it
|
||||
@register.filter
|
||||
def get_ballot_value(value):
|
||||
return pow(10, value-1)
|
||||
def get_ballot_value(option_no, options_count):
|
||||
ballot_value = ""
|
||||
|
||||
for i in range(options_count):
|
||||
|
||||
if (i+1) == option_no:
|
||||
ballot_value = ballot_value + "1"
|
||||
else:
|
||||
ballot_value = ballot_value + "0"
|
||||
|
||||
if not i == (options_count-1):
|
||||
ballot_value = ballot_value + ","
|
||||
|
||||
return ballot_value
|
||||
|
|
|
@ -1,37 +1,24 @@
|
|||
from django.conf.urls import url
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = 'polls'
|
||||
|
||||
#urlpatterns = [
|
||||
# url(r'^$', views.index, name='index'),
|
||||
# ex: /polls/5/
|
||||
# url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
|
||||
# ex: /polls/5/results/
|
||||
# url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
|
||||
# ex: /polls/5/vote/
|
||||
# url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
|
||||
#]
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^vote/(?P<poll_id>[0-9]+)/$', views.test_poll_vote, name='vote-poll'),
|
||||
url(r'^(?P<pk>[0-9]+)/$', views.EventDetailView.as_view(), name='view-event'),
|
||||
url(r'^(?P<pk>[0-9]+)/polls$', views.EventDetailPollsView.as_view(), name='event-polls'),
|
||||
url(r'^(?P<pk>[0-9]+)/organisers$', views.EventDetailOrganisersView.as_view(), name='event-organisers'),
|
||||
url(r'^$', views.EventListView.as_view(), name='index'),
|
||||
url(r'^$', login_required(views.EventListView.as_view()), name='index'),
|
||||
url(r'^create/$', login_required(views.create_event), name='create-event'),
|
||||
url(r'^(?P<event_id>[0-9]+)/decrypt/$', login_required(views.event_trustee_decrypt), name='decrypt-event'),
|
||||
url(r'^(?P<event_id>[0-9]+)/prepare/$', login_required(views.event_trustee_setup), name='prepare-event'),
|
||||
url(r'^(?P<event_id>[0-9]+)/encrypt/$', login_required(views.event_addec), name='enc-event'),
|
||||
url(r'^(?P<pk>[0-9]+)/launch/$', views.EventDetailLaunchView.as_view(), name='launch-event'),
|
||||
url(r'^edit/(?P<event_id>[0-9]+)/$', login_required(views.edit_event), name='edit-event'),
|
||||
url(r'^delete/(?P<event_id>[0-9]+)/$', login_required(views.del_event), name='del-event'),
|
||||
url(r'^(?P<pk>[0-9]+)/$', login_required(views.EventDetailView.as_view()), name='view-event'),
|
||||
url(r'^(?P<pk>[0-9]+)/polls/$', login_required(views.EventDetailPollsView.as_view()), name='event-polls'),
|
||||
url(r'^(?P<pk>[0-9]+)/entities/$', login_required(views.EventDetailEntitiesView.as_view()), name='event-entities'),
|
||||
url(r'^(?P<pk>[0-9]+)/advanced/$', login_required(views.EventDetailAdvancedView.as_view()), name='event-advanced'),
|
||||
url(r'^(?P<event_id>[0-9]+)/end/$', login_required(views.event_end), name='end-event'),
|
||||
url(r'^(?P<event_id>[0-9]+)/results/$', login_required(views.results), name='event-results'),
|
||||
url(r'^(?P<event_id>[0-9]+)/edit/$', login_required(views.edit_event), name='edit-event'),
|
||||
url(r'^(?P<event_id>[0-9]+)/delete/$', login_required(views.del_event), name='del-event'),
|
||||
url(r'^(?P<event_id>[0-9]+)/decrypt/$', views.event_trustee_decrypt, name='decrypt-event'),
|
||||
url(r'^(?P<event_id>[0-9]+)/prepare/$', views.event_trustee_setup, name='prepare-event'),
|
||||
url(r'^(?P<event_id>[0-9]+)/poll/(?P<poll_num>[0-9]+)/vote/$', views.event_vote, name='event-vote'),
|
||||
url(r'^(?P<event_id>[0-9]+)/create/poll/$', login_required(views.manage_questions), name='create-poll'),
|
||||
url(r'^(?P<event_id>[0-9]+)/poll/(?P<poll_num>[0-9]+)/$', login_required(views.view_poll), name='view-poll'),
|
||||
url(r'^(?P<event_id>[0-9]+)/poll/(?P<poll_num>[0-9]+)/edit$', login_required(views.edit_poll), name='edit-poll'),
|
||||
#url(r'^(?P<pk>[0-9]+)/$', login_required(views.DetailView.as_view()), name='detail'),
|
||||
#url(r'^(?P<pk>[0-9]+)/results/$', login_required(views.ResultsView.as_view()), name='results'),
|
||||
#url(r'^(?P<question_id>[0-9]+)/vote/$', login_required(views.vote), name='vote'),
|
||||
url(r'^(?P<event_id>[0-9]+)/poll/(?P<poll_num>[0-9]+)/edit$', login_required(views.edit_poll), name='edit-poll')
|
||||
]
|
||||
|
|
|
@ -539,12 +539,9 @@ class EventModelAdaptor:
|
|||
# Extract the list of trustees
|
||||
trustees_list = self.form_data.pop('trustee-email-input')
|
||||
|
||||
for trustee in trustees_list:
|
||||
if trustee != '':
|
||||
if EmailUser.objects.filter(email=trustee).exists():
|
||||
self.trustees.append(EmailUser.objects.filter(email=trustee).get())
|
||||
else:
|
||||
self.trustees.append(EmailUser(email=trustee))
|
||||
for trustee_email in trustees_list:
|
||||
if trustee_email != '':
|
||||
self.trustees.append(trustee_email)
|
||||
|
||||
# Extract the email list of voters
|
||||
voters_csv_string = self.form_data.pop('voters-list-input')[0].replace(' ', '')
|
||||
|
@ -552,17 +549,21 @@ class EventModelAdaptor:
|
|||
|
||||
for voter_email in voters_email_list:
|
||||
if voter_email != '':
|
||||
if EmailUser.objects.filter(email=voter_email).exists():
|
||||
self.voters.append(EmailUser.objects.filter(email=voter_email).get())
|
||||
else:
|
||||
self.voters.append(EmailUser(email=voter_email))
|
||||
self.voters.append(voter_email)
|
||||
|
||||
# Create the Event model object - this does not persist it to the DB
|
||||
creator = ""
|
||||
if self.user.first_name is not None:
|
||||
creator += self.user.first_name + " "
|
||||
|
||||
if self.user.last_name is not None:
|
||||
creator += self.user.last_name
|
||||
|
||||
self.event = Event(start_time=self.starts_at,
|
||||
end_time=self.ends_at,
|
||||
title=self.event_name,
|
||||
EID=self.identifier,
|
||||
creator=self.user.first_name + ' ' + self.user.last_name,
|
||||
creator=creator,
|
||||
c_email=self.user.email,
|
||||
trustees=voters_csv_string)
|
||||
|
||||
|
@ -629,20 +630,21 @@ class EventModelAdaptor:
|
|||
# so it can just be added
|
||||
self.event.users_organisers = self.organisers
|
||||
|
||||
# Add the list of trustees to the event, making sure they're instantiated
|
||||
# Add the list of trustees to the event
|
||||
db_trustees = list()
|
||||
for trustee in self.trustees:
|
||||
if not EmailUser.objects.filter(email=trustee.email).exists():
|
||||
trustee.save()
|
||||
user, created = EmailUser.objects.get_or_create(email=trustee)
|
||||
db_trustees.append(user)
|
||||
|
||||
self.event.users_trustees = self.trustees
|
||||
self.event.users_trustees = db_trustees
|
||||
|
||||
# Add the list of voters to the event, making sure they're instantiated
|
||||
# Additionally, generating the AccessKey for voters
|
||||
# Add the list of voters to the event
|
||||
db_voters = list()
|
||||
for voter in self.voters:
|
||||
if not EmailUser.objects.filter(email=voter.email).exists():
|
||||
voter.save()
|
||||
user, created = EmailUser.objects.get_or_create(email=voter)
|
||||
db_voters.append(user)
|
||||
|
||||
self.event.voters = self.voters
|
||||
self.event.voters = db_voters
|
||||
|
||||
# Extract all the poll data for the event and associated poll option data
|
||||
# This can only be done at this point as the event has been persisted
|
||||
|
|
|
@ -2,25 +2,23 @@ import urllib
|
|||
import urllib2
|
||||
import json
|
||||
|
||||
from io import StringIO
|
||||
from django.contrib import messages
|
||||
from django.http import HttpResponseRedirect, HttpResponse, Http404
|
||||
from django.http.response import HttpResponseNotAllowed
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.shortcuts import get_object_or_404, render, render_to_response
|
||||
from django.utils import timezone
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.views import generic
|
||||
from django.conf import settings
|
||||
from django.core import serializers
|
||||
|
||||
from .forms import EventForm, PollForm, OptionFormset, QuestionFormset, OrganiserFormSet, TrusteeFormSet, VoteForm, EventSetupForm, EventEditForm, DecryptionFormset, DecryptionFormSetHelper
|
||||
from .models import Event, Poll, PollOption, EmailUser, Ballot, TrusteeKey, Decryption
|
||||
from .forms import PollForm, OptionFormset, VoteForm, EventSetupForm, EventEditForm
|
||||
from .models import Event, Poll, Ballot, EncryptedVote, TrusteeKey, TrusteeSK
|
||||
from allauthdemo.auth.models import DemoUser
|
||||
|
||||
from .tasks import email_trustees_prep, update_EID, generate_combpk, generate_enc, tally_results
|
||||
from .tasks import email_trustees_prep, update_EID, generate_combpk, event_ended, create_ballots, create_ballots_for_poll, email_voters_vote_url, gen_event_sk_and_dec
|
||||
|
||||
from .utils.EventModelAdaptor import EventModelAdaptor
|
||||
|
||||
|
||||
class EventListView(generic.ListView):
|
||||
|
||||
model = Event
|
||||
|
@ -30,6 +28,7 @@ class EventListView(generic.ListView):
|
|||
#context['now'] = timezone.now()
|
||||
return context
|
||||
|
||||
|
||||
class EventDetailView(generic.DetailView):
|
||||
template_name="polls/event_detail_details.html"
|
||||
model = Event
|
||||
|
@ -37,38 +36,32 @@ class EventDetailView(generic.DetailView):
|
|||
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"
|
||||
|
||||
#context['now'] = timezone.now()
|
||||
return context
|
||||
|
||||
|
||||
class EventDetailPollsView(EventDetailView):
|
||||
template_name="polls/event_detail_polls.html"
|
||||
template_name = "polls/event_detail_polls.html"
|
||||
|
||||
class EventDetailOrganisersView(EventDetailView):
|
||||
template_name="polls/event_detail_organisers.html"
|
||||
|
||||
class EventDetailLaunchView(EventDetailView):
|
||||
template_name="polls/event_detail_launch.html"
|
||||
class EventDetailEntitiesView(EventDetailView):
|
||||
template_name = "polls/event_detail_entities.html"
|
||||
|
||||
|
||||
class EventDetailAdvancedView(EventDetailView):
|
||||
template_name = "polls/event_detail_advanced.html"
|
||||
|
||||
|
||||
class PollDetailView(generic.View):
|
||||
|
||||
model = Poll
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(PollDetailView, self).get_context_data(**kwargs)
|
||||
#context['now'] = timezone.now()
|
||||
context['form'] = VoteForm(instance=self.object)
|
||||
context['poll_count'] = self.object.event.polls.all().count()
|
||||
return context
|
||||
|
||||
#my_value = self.kwargs.get('key', 'default_value')
|
||||
|
||||
def test_poll_detail(request, event_id, poll_num, key=None):
|
||||
context = {}
|
||||
context['form'] = VoteForm(instance=self.object)
|
||||
context['poll_count'] = self.object.event.polls.all().count()
|
||||
return render(request, "polls/event_setup.html", context)
|
||||
|
||||
def util_get_poll_by_event_index(event, poll_num):
|
||||
try:
|
||||
|
@ -80,78 +73,114 @@ def util_get_poll_by_event_index(event, poll_num):
|
|||
return None
|
||||
return poll
|
||||
|
||||
|
||||
def edit_poll(request, event_id, poll_num):
|
||||
event = get_object_or_404(Event, pk=event_id)
|
||||
event_poll_count = event.polls.all().count()
|
||||
poll = util_get_poll_by_event_index(event, poll_num)
|
||||
|
||||
if (poll == None):
|
||||
raise Http404("Poll does not exist")
|
||||
|
||||
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})
|
||||
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")
|
||||
|
||||
def view_poll(request, event_id, poll_num):
|
||||
#return HttpResponse(param("012345"))
|
||||
#return HttpResponse(combpk(param("012345"), "ABzqvL+pqTi+DNLLRcM62RwCoaZTaXVbOs3sk4fc0+Dc 0 AAaQd6S1x+bcgnkDp2ev5mTt34ICQdZIzP9GaqG4x5sy 0" "ABhQay9jI4pZvkAETNwfo8iwJ8eBMkjqplqAiu/FZxMy 0 ABPxj0jVj3rt0VW54iv4tV02gYtujnR41t5gf97asrPs 0 ABfoiW03bsYIUgfAThmjurmOViKy9L89vfkIavhQIblm 1 ABhQay9jI4pZvkAETNwfo8iwJ8eBMkjqplqAiu/FZxMy 0 ABPxj0jVj3rt0VW54iv4tV02gYtujnR41t5gf97asrPs 0 ABfoiW03bsYIUgfAThmjurmOViKy9L89vfkIavhQIblm 1 ABhQay9jI4pZvkAETNwfo8iwJ8eBMkjqplqAiu/FZxMy 0 ABPxj0jVj3rt0VW54iv4tV02gYtujnR41t5gf97asrPs 0 ABfoiW03bsYIUgfAThmjurmOViKy9L89vfkIavhQIblm 1"))
|
||||
#return HttpResponse(addec("ACMW70Yj3+mJ/FO+6VOSDGYPYHf7NoTXdpInbfzUqYpH 0 ABV4Mo496B0FW3AW/7gY6Fs+oz6BwfwilonMYeriUyV/ 0 AAg+bdGhs3sxSxAc/wcKdBNUy+el8A2b4yVYShNOb8uX 0 AAspJbn5V2AaY4CgLkzCkHwUWbC5nyxrBzw+o4Az8HVM 1 ABKI7o5Yhgi44XwpFnPpLnH0/czbXA8y5vM4ucV8vojo 1 AAwVrT9+dcQsqRZYoI7+QsJvWOgd7JaJpfI6envmC2jU 1 ABIZO0DK4OrdROD805of6iRk2RenonGYmo2qG2IB1sj/ 1 ACMUHQdjGN0wyCd2AgDHMk9u0TpnywNVtamHWopGho8L 0 ABNT5lbE4siC3QklQXRvTwSQPwtme91+UrIr9iXT3y84 1 ABib0mmQ9ZVCrErqFwDgoRp3jHPpjHGQR2vsMVlwM+vI 0 ABvf3cg1NSS8fn6EKJNnTomeoflcEY1WBxkPPKrBBFl+ 0 ACBUZAtolN4HNh+mw4jLZuHzD+/rYHKR5av16PUc6BJF 0", "2"))
|
||||
#return HttpResponse(tally("ACNQLLQlh+lNm1Dc+X+dEI0ECVLTkxRHjRnzX1OA+HtW 0 AAWOsUZK/G/cjhUee/gPAXop3Bc0CTVG3iDdQxD6+XqV 0", "ACNQLLQlh+lNm1Dc+X+dEI0ECVLTkxRHjRnzX1OA+HtW 0 0 2", "2"))
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
|
||||
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]))
|
||||
|
||||
|
||||
def event_vote(request, event_id, poll_num):
|
||||
event = get_object_or_404(Event, pk=event_id)
|
||||
if (not event.prepared):
|
||||
|
||||
if not event.prepared:
|
||||
messages.add_message(request, messages.WARNING, "This Event isn\'t ready for voting yet.")
|
||||
return HttpResponseRedirect(reverse("user_home"))
|
||||
|
||||
event_poll_count = event.polls.all().count()
|
||||
prev_poll_index, next_poll_index = False, False
|
||||
can_vote, has_voted, voter_email, vote_count = False, False, "", 0
|
||||
can_vote, has_voted, voter_email = False, False, ""
|
||||
poll = util_get_poll_by_event_index(event, poll_num)
|
||||
|
||||
if (poll == None):
|
||||
raise Http404("Poll does not exist")
|
||||
if poll is None:
|
||||
messages.add_message(request, messages.ERROR, "There was an error loading the voting page.")
|
||||
return HttpResponseRedirect(reverse("user_home"))
|
||||
|
||||
form = VoteForm(instance=poll)
|
||||
poll_num = int(poll_num) # now known to be safe as it suceeded in the util function
|
||||
poll_num = int(poll_num) # now known to be safe as it succeeded in the util function
|
||||
|
||||
if (poll_num > 1):
|
||||
if poll_num > 1:
|
||||
prev_poll_index = (poll_num - 1)
|
||||
if (poll_num < event_poll_count):
|
||||
if poll_num < event_poll_count:
|
||||
next_poll_index = (poll_num + 1)
|
||||
|
||||
access_key = request.GET.get('key', None)
|
||||
email_key = event.keys.filter(key=access_key)
|
||||
vote_count = Ballot.objects.filter(poll=poll, cast=True).count()
|
||||
|
||||
if (email_key.exists() and event.voters.filter(email=email_key[0].user.email).exists()):
|
||||
ballot = Ballot.objects.filter(voter=email_key[0].user, poll=poll)
|
||||
if (ballot.exists() and ballot[0].cast):
|
||||
has_voted = True
|
||||
|
||||
if (access_key and email_key.exists()): #or (can_vote(request.user, event))
|
||||
ballot = None
|
||||
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 (request.method == "POST"):
|
||||
form = VoteForm(request.POST, instance=poll)
|
||||
if (email_key.exists()):
|
||||
#return HttpResponse(email_key[0].key)
|
||||
ballot = Ballot.objects.get_or_create(voter=email_key[0].user, poll=poll)[0]
|
||||
# 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:
|
||||
messages.add_message(request, messages.ERROR, "You don\'t have permission to vote in this event.")
|
||||
return HttpResponseRedirect(reverse("user_home"))
|
||||
|
||||
if (form.is_valid()):
|
||||
ballot.cipher_text_c1 = request.POST["cipher_text_c1"]
|
||||
ballot.cipher_text_c2 = request.POST["cipher_text_c2"]
|
||||
ballot.cast = True
|
||||
ballot.save()
|
||||
if (next_poll_index):
|
||||
return HttpResponseRedirect(reverse('polls:view-poll', kwargs={'event_id': event.id, 'poll_num': next_poll_index }) + "?key=" + email_key[0].key)
|
||||
else:
|
||||
return HttpResponse("Voted successfully!") # finished all polls in event
|
||||
if request.method == "POST":
|
||||
if ballot is None:
|
||||
ballot = Ballot.objects.get_or_create(voter=email_key[0].user, poll=poll)
|
||||
|
||||
return render(request, "polls/poll_detail.html",
|
||||
{"object": poll, "poll_num": poll_num , "event": event, "form": form, "poll_count": event.polls.all().count(),
|
||||
"prev_index": prev_poll_index , "next_index": next_poll_index,
|
||||
"can_vote": can_vote, "voter_email": voter_email, "has_voted": has_voted, "vote_count": vote_count
|
||||
# Will store the fragments of the encoding scheme that define the vote
|
||||
encrypted_vote = EncryptedVote.objects.get_or_create(ballot=ballot[0])[0]
|
||||
|
||||
# Clear any existing fragments - a voter changing their vote
|
||||
encrypted_vote.fragment.all().delete()
|
||||
|
||||
# Add in the new ciphers
|
||||
fragment_count = int(request.POST['vote_frag_count'])
|
||||
for i in range(fragment_count):
|
||||
i_str = str(i)
|
||||
|
||||
cipher_c1 = request.POST['cipher_c1_frag_' + i_str]
|
||||
cipher_c2 = request.POST['cipher_c2_frag_' + i_str]
|
||||
|
||||
encrypted_vote.fragment.create(encrypted_vote=encrypted_vote,
|
||||
cipher_text_c1=cipher_c1,
|
||||
cipher_text_c2=cipher_c2)
|
||||
|
||||
ballot[0].cast = True
|
||||
ballot[0].save()
|
||||
|
||||
if next_poll_index:
|
||||
return HttpResponseRedirect(reverse('polls:event-vote', kwargs={'event_id': event.id, 'poll_num': next_poll_index }) + "?key=" + email_key[0].key)
|
||||
else:
|
||||
# The user has finished voting in the event
|
||||
success_msg = 'You have successfully cast your vote(s)!'
|
||||
messages.add_message(request, messages.SUCCESS, success_msg)
|
||||
|
||||
return HttpResponseRedirect(reverse("user_home"))
|
||||
|
||||
return render(request, "polls/event_vote.html",
|
||||
{
|
||||
"object": poll, "poll_num": poll_num, "event": event, "poll_count": event.polls.all().count(),
|
||||
"prev_index": prev_poll_index, "next_index": next_poll_index, "min_selection": poll.min_num_selections,
|
||||
"max_selection": poll.max_num_selections, "can_vote": can_vote, "voter_email": voter_email,
|
||||
"has_voted": has_voted
|
||||
})
|
||||
|
||||
|
||||
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)
|
||||
|
@ -178,68 +207,82 @@ def event_trustee_setup(request, event_id):
|
|||
# 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)
|
||||
# TODO: Create Celery task that generates voting URLs for voters as well as creates the ballots
|
||||
email_voters_vote_url.delay(event.voters.all(), event)
|
||||
|
||||
success_msg = 'You have successfully submitted your public key for this event'
|
||||
success_msg = 'You have successfully submitted your public key for this event!'
|
||||
messages.add_message(request, messages.SUCCESS, success_msg)
|
||||
|
||||
# This re-direct may not be appropriate for trustees that don't have logins
|
||||
return HttpResponseRedirect(reverse("user_home"))
|
||||
else:
|
||||
form = EventSetupForm()
|
||||
return render(request, "polls/event_setup.html", {"event": event, "form": form})
|
||||
return render(request, "polls/event_setup.html", {"event": event, "form": form, "user_email": email_key[0].user.email})
|
||||
|
||||
#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"))
|
||||
|
||||
def event_addec(request, event_id):
|
||||
|
||||
def event_end(request, event_id):
|
||||
event = get_object_or_404(Event, pk=event_id)
|
||||
for poll in event.polls.all():
|
||||
generate_enc.delay(poll)
|
||||
return HttpResponse("Generating enc.")
|
||||
|
||||
if not event.ended:
|
||||
event_ended.delay(event)
|
||||
|
||||
# Mark the event as ended
|
||||
event.ended = True
|
||||
event.save()
|
||||
|
||||
return HttpResponseRedirect(reverse('polls:view-event', args=[event_id]))
|
||||
|
||||
|
||||
# Returns a JSONed version of the results
|
||||
def results(request, event_id):
|
||||
event = get_object_or_404(Event, pk=event_id)
|
||||
polls = event.polls.all()
|
||||
|
||||
results = ""
|
||||
results += "{\"polls\":["
|
||||
for poll in polls:
|
||||
results += poll.enc
|
||||
|
||||
results += "]}"
|
||||
|
||||
return HttpResponse(results)
|
||||
|
||||
|
||||
def event_trustee_decrypt(request, event_id):
|
||||
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)
|
||||
if (email_key.exists() and event.users_trustees.filter(email=email_key[0].user.email).exists()):
|
||||
if (Decryption.objects.filter(event=event, user=email_key[0].user).exists()):
|
||||
messages.add_message(request, messages.WARNING, 'You have already provided your decryptions for this event')
|
||||
#if (event.decryptions.count() == (event.polls.count() * event.users_trustees.count())):
|
||||
# tally_results.delay(event) # all keys are in
|
||||
return HttpResponseRedirect(reverse("user_home"))
|
||||
elif (request.method == "GET"):
|
||||
initial = []
|
||||
for poll in event.polls.all():
|
||||
initial.append({'text': poll.enc })
|
||||
formset = DecryptionFormset(initial=initial)
|
||||
else:
|
||||
formset = DecryptionFormset(request.POST)
|
||||
data = []
|
||||
for form in formset:
|
||||
if form.is_valid():
|
||||
data.append(form.cleaned_data.get('text'))
|
||||
if (len(data) == event.polls.count()):
|
||||
for dec, poll in zip(data, event.polls.all()):
|
||||
Decryption.objects.get_or_create(user=email_key[0].user, event=event, poll=poll, text=dec)
|
||||
messages.add_message(request, messages.SUCCESS, 'Decryption complete.')
|
||||
if (event.decryptions.count() == (event.polls.count() * event.users_trustees.count())):
|
||||
tally_results.delay(event) # all keys are in
|
||||
else:
|
||||
messages.add_message(request, messages.ERROR, 'You didn\'t provide decryptions for every poll. Please try again.')
|
||||
return HttpResponseRedirect(reverse("user_home"))
|
||||
return render(request, "polls/event_decrypt.html", {"event": event, "formset": formset, "helper": DecryptionFormSetHelper() })
|
||||
|
||||
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 TrusteeSK.objects.filter(event=event, trustee=email_key[0].user).exists():
|
||||
messages.add_message(request, messages.WARNING, 'You have already provided your decryption key for this event')
|
||||
return HttpResponseRedirect(reverse("user_home"))
|
||||
elif request.method == "GET":
|
||||
return render(request, "polls/event_decrypt.html", {"event": event, "user_email": email_key[0].user.email})
|
||||
elif request.method == "POST":
|
||||
sk = request.POST['secret-key']
|
||||
|
||||
TrusteeSK.objects.create(event=event,
|
||||
trustee=email_key[0].user,
|
||||
key=sk)
|
||||
|
||||
if event.trustee_sk.count() == event.users_trustees.count():
|
||||
# Generate the event SK and decrypt the event to tally the results
|
||||
gen_event_sk_and_dec.delay(event)
|
||||
|
||||
messages.add_message(request, messages.SUCCESS, 'Your secret key has been successfully submitted')
|
||||
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"))
|
||||
|
||||
def test_poll_vote(request, poll_id):
|
||||
poll = get_object_or_404(Poll, pk=poll_id)
|
||||
form = VoteForm(instance=poll)
|
||||
return render(request, "polls/vote_poll.html", {"vote_form": form, "poll": poll})
|
||||
|
||||
def manage_questions(request, event_id):
|
||||
|
||||
|
@ -262,7 +305,7 @@ def manage_questions(request, event_id):
|
|||
formset = OptionFormset(request.POST, prefix="formset_organiser", instance=poll)
|
||||
if formset.is_valid():
|
||||
formset.save()
|
||||
#create_ballots.delay(poll)
|
||||
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]))
|
||||
|
||||
|
@ -274,6 +317,7 @@ def manage_questions(request, event_id):
|
|||
else:
|
||||
return HttpResponseNotAllowed()
|
||||
|
||||
|
||||
def render_invalid(request, events, demo_users, invalid_fields):
|
||||
return render(request,
|
||||
"polls/create_event.html",
|
||||
|
@ -285,6 +329,7 @@ def render_invalid(request, events, demo_users, invalid_fields):
|
|||
"invalid_fields": invalid_fields
|
||||
})
|
||||
|
||||
|
||||
def create_event(request):
|
||||
# Obtain context data for the rendering of the html template and validation
|
||||
events = Event.objects.all()
|
||||
|
@ -347,6 +392,7 @@ def create_event(request):
|
|||
else:
|
||||
return HttpResponseNotAllowed()
|
||||
|
||||
|
||||
def edit_event(request, event_id):
|
||||
event = get_object_or_404(Event, pk=event_id)
|
||||
if request.method == "GET":
|
||||
|
@ -384,7 +430,6 @@ def edit_event(request, 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)
|
||||
|
||||
#class CreatePoll(generic.View):
|
||||
|
||||
def del_event(request, event_id):
|
||||
event = get_object_or_404(Event, pk=event_id)
|
||||
|
@ -392,9 +437,4 @@ def del_event(request, event_id):
|
|||
return render(request, "polls/del_event.html", {"event_title": event.title, "event_id": event.id})
|
||||
elif request.method == "POST":
|
||||
event.delete()
|
||||
return HttpResponseRedirect(reverse('polls:index'))
|
||||
|
||||
def can_vote(user, event):
|
||||
if event.voters.filter(email=user.email).exists():
|
||||
return True
|
||||
return False
|
||||
return HttpResponseRedirect(reverse('polls:index'))
|
Reference in a new issue