diff --git a/.gitignore b/.gitignore index 681947f7f..8db764f4e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,8 @@ deploy-latest.sh .DS_Store *~ media/* -venv +venv* celerybeat-* env.sh -.cache \ No newline at end of file +.cache +.idea/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index be482e4e4..476acaf46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,46 @@ -sudo: false language: python -python: - - "2.7" -# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors +os: linux + +jobs: + include: + - python: "3.7" + dist: xenial + addons: + postgresql: "9.5" + - python: "3.7" + dist: xenial + addons: + postgresql: "9.6" + - python: "3.7" + dist: xenial + addons: + postgresql: "10" + - python: "3.8" + dist: bionic + addons: + postgresql: "9.5" + - python: "3.8" + dist: bionic + addons: + postgresql: "9.6" + - python: "3.8" + dist: bionic + addons: + postgresql: "10" + - python: "3.8" + dist: bionic + addons: + postgresql: "11" + +before_install: + - export BOTO_CONFIG=/dev/null + install: - - pip install setuptools==24.3.1 - - pip install -r requirements.txt -# command to run tests, e.g. python setup.py test -script: "python manage.py test" -addons: - postgresql: "9.3" + - pip3 install --upgrade pip + - pip3 install -r requirements.txt + - pip3 freeze + before_script: - psql -c 'create database helios;' -U postgres + +script: "python3 -Wall manage.py test -v 2" diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 61ac19608..a0c88cf51 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -7,3 +7,6 @@ Significant contributors: - Olivier de Marneffe - Emily Stark, Mike Hamburg, Tom Wu, and Dan Boneh for SJCL and integration of javascript crypto. - Nicholas Chang-Fong and Aleksander Essex for security reports and fixes. +- Shirley Chaves +- Marco Ciotola +- Lucas Araujo diff --git a/INSTALL.md b/INSTALL.md index fdb0ae10f..c14d97659 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,4 +1,8 @@ -* install PostgreSQL 8.3+ +* install PostgreSQL 9.5+ + +* install Rabbit MQ + This is needed for celery to work, which does background processing such as + the processing of uploaded list-of-voter CSV files. * make sure you have virtualenv installed: http://www.virtualenv.org/en/latest/ @@ -7,10 +11,22 @@ http://www.virtualenv.org/en/latest/ * cd into the helios-server directory -* create a virtualenv: +* install Python3.6 including dev, pip, and venv + +``` +sudo apt install python3.6 python3.6-venv python3.6-pip python3.6-venv +``` + +* create a virtualenv + +``` +python3.6 -m venv $(pwd)/venv +``` + +* you'll also need Postgres dev libraries. For example on Ubuntu: ``` -virtualenv venv +sudo apt install libpq-dev ``` * activate virtual environment @@ -45,6 +61,6 @@ python manage.py runserver ** set up oauth2 credentials as a web application, with your origin, e.g. https://myhelios.example.com, and your auth callback, which, based on our example, is https://myhelios.example.com/auth/after/ -** still in the developer console, enable the Google+ API. +** still in the developer console, enable the Google+ API and Google People API. -** set the GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET configuration variables accordingly. \ No newline at end of file +** set the GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET configuration variables accordingly. diff --git a/Procfile b/Procfile index 63d8d0cf6..04ed6cbca 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,2 @@ web: gunicorn wsgi:application -b 0.0.0.0:$PORT -w 8 -worker: python manage.py celeryd -E -B --beat --concurrency=1 \ No newline at end of file +worker: celery --app helios worker --events --beat --concurrency 1 \ No newline at end of file diff --git a/README.md b/README.md index 5c90a9c20..9a5519bfc 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,6 @@ Helios is an end-to-end verifiable voting system. -![Travis Build Status](https://travis-ci.org/benadida/helios-server.svg?branch=master) +[![Travis Build Status](https://travis-ci.org/benadida/helios-server.svg?branch=master)](https://travis-ci.org/benadida/helios-server) [![Stories in Ready](https://badge.waffle.io/benadida/helios-server.png?label=ready&title=Ready)](https://waffle.io/benadida/helios-server) diff --git a/extract-passwords-for-email.py b/extract-passwords-for-email.py index d72705792..dc9ac9564 100644 --- a/extract-passwords-for-email.py +++ b/extract-passwords-for-email.py @@ -5,12 +5,16 @@ # python extract-passwords-for-email.py # -from django.core.management import setup_environ -import settings, sys, csv +import sys -setup_environ(settings) +import csv +import django +import os -from helios.models import * +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") +django.setup() + +from helios.models import Election election_uuid = sys.argv[1] email = sys.argv[2] diff --git a/helios/__init__.py b/helios/__init__.py index 06f05140b..21f21bc59 100644 --- a/helios/__init__.py +++ b/helios/__init__.py @@ -1,7 +1,9 @@ - from django.conf import settings -from django.core.urlresolvers import reverse -from helios.views import election_shortcut +# This will make sure the app is always imported when +# Django starts so that shared_task will use this app. +from .celery_app import app as celery_app + +__all__ = ('celery_app', 'TEMPLATE_BASE', 'ADMIN_ONLY', 'VOTERS_UPLOAD', 'VOTERS_EMAIL',) TEMPLATE_BASE = settings.HELIOS_TEMPLATE_BASE or "helios/templates/base.html" diff --git a/helios/apps.py b/helios/apps.py new file mode 100644 index 000000000..900974328 --- /dev/null +++ b/helios/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + +class HeliosConfig(AppConfig): + name = 'helios' + verbose_name = "Helios" diff --git a/helios/celery_app.py b/helios/celery_app.py new file mode 100644 index 000000000..89f0ecb54 --- /dev/null +++ b/helios/celery_app.py @@ -0,0 +1,21 @@ +import os + +# set the default Django settings module for the 'celery' program. +from celery import Celery + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings') + +app = Celery() + +# Using a string here means the worker doesn't have to serialize +# the configuration object to child processes. +# - namespace='CELERY' means all celery-related configuration keys +# should have a `CELERY_` prefix. +app.config_from_object('django.conf:settings', namespace='CELERY') + +# Load task modules from all registered Django app configs. +app.autodiscover_tasks() + +@app.task(bind=True) +def debug_task(self): + print('Request: {0!r}'.format(self.request)) diff --git a/helios/crypto/algs.py b/helios/crypto/algs.py index acac4f44b..b7439d9bd 100644 --- a/helios/crypto/algs.py +++ b/helios/crypto/algs.py @@ -7,157 +7,61 @@ ben@adida.net """ -import math, hashlib, logging -import randpool, number +import logging -import numtheory +from Crypto.Hash import SHA1 +from Crypto.Util import number -# some utilities -class Utils: - RAND = randpool.RandomPool() - - @classmethod - def random_seed(cls, data): - cls.RAND.add_event(data) - - @classmethod - def random_mpz(cls, n_bits): - low = 2**(n_bits-1) - high = low * 2 - - # increment and find a prime - # return randrange(low, high) - - return number.getRandomNumber(n_bits, cls.RAND.get_bytes) - - @classmethod - def random_mpz_lt(cls, max): - # return randrange(0, max) - n_bits = int(math.floor(math.log(max, 2))) - return (number.getRandomNumber(n_bits, cls.RAND.get_bytes) % max) - - @classmethod - def random_prime(cls, n_bits): - return number.getPrime(n_bits, cls.RAND.get_bytes) - - @classmethod - def is_prime(cls, mpz): - #return numtheory.miller_rabin(mpz) - return number.isPrime(mpz) - - @classmethod - def xgcd(cls, a, b): - """ - Euclid's Extended GCD algorithm - """ - mod = a%b - - if mod == 0: - return 0,1 - else: - x,y = cls.xgcd(b, mod) - return y, x-(y*(a/b)) - - @classmethod - def inverse(cls, mpz, mod): - # return cls.xgcd(mpz,mod)[0] - return number.inverse(mpz, mod) - - @classmethod - def random_safe_prime(cls, n_bits): - p = None - q = None - - while True: - p = cls.random_prime(n_bits) - q = (p-1)/2 - if cls.is_prime(q): - return p - - @classmethod - def random_special_prime(cls, q_n_bits, p_n_bits): - p = None - q = None - - z_n_bits = p_n_bits - q_n_bits - - q = cls.random_prime(q_n_bits) - - while True: - z = cls.random_mpz(z_n_bits) - p = q*z + 1 - if cls.is_prime(p): - return p, q, z +from helios.crypto.utils import random +from helios.utils import to_json class ElGamal: def __init__(self): - self.p = None - self.q = None - self.g = None - - @classmethod - def generate(cls, n_bits): - """ - generate an El-Gamal environment. Returns an instance - of ElGamal(), with prime p, group size q, and generator g - """ - - EG = ElGamal() - - # find a prime p such that (p-1)/2 is prime q - EG.p = Utils.random_safe_prime(n_bits) - - # q is the order of the group - # FIXME: not always p-1/2 - EG.q = (EG.p-1)/2 - - # find g that generates the q-order subgroup - while True: - EG.g = Utils.random_mpz_lt(EG.p) - if pow(EG.g, EG.q, EG.p) == 1: - break - - return EG + self.p = None + self.q = None + self.g = None def generate_keypair(self): - """ - generates a keypair in the setting - """ + """ + generates a keypair in the setting + """ - keypair = EGKeyPair() - keypair.generate(self.p, self.q, self.g) + keypair = EGKeyPair() + keypair.generate(self.p, self.q, self.g) - return keypair + return keypair def toJSONDict(self): - return {'p': str(self.p), 'q': str(self.q), 'g': str(self.g)} + return {'p': str(self.p), 'q': str(self.q), 'g': str(self.g)} @classmethod def fromJSONDict(cls, d): - eg = cls() - eg.p = int(d['p']) - eg.q = int(d['q']) - eg.g = int(d['g']) - return eg + eg = cls() + eg.p = int(d['p']) + eg.q = int(d['q']) + eg.g = int(d['g']) + return eg + class EGKeyPair: def __init__(self): - self.pk = EGPublicKey() - self.sk = EGSecretKey() + self.pk = EGPublicKey() + self.sk = EGSecretKey() def generate(self, p, q, g): - """ - Generate an ElGamal keypair - """ - self.pk.g = g - self.pk.p = p - self.pk.q = q + """ + Generate an ElGamal keypair + """ + self.pk.g = g + self.pk.p = p + self.pk.q = q + + self.sk.x = random.mpz_lt(q) + self.pk.y = pow(g, self.sk.x, p) - self.sk.x = Utils.random_mpz_lt(q) - self.pk.y = pow(g, self.sk.x, p) + self.sk.pk = self.pk - self.sk.pk = self.pk class EGPublicKey: def __init__(self): @@ -166,7 +70,7 @@ def __init__(self): self.g = None self.q = None - def encrypt_with_r(self, plaintext, r, encode_message= False): + def encrypt_with_r(self, plaintext, r, encode_message=False): """ expecting plaintext.m to be a big integer """ @@ -175,13 +79,13 @@ def encrypt_with_r(self, plaintext, r, encode_message= False): # make sure m is in the right subgroup if encode_message: - y = plaintext.m + 1 - if pow(y, self.q, self.p) == 1: - m = y - else: - m = -y % self.p + y = plaintext.m + 1 + if pow(y, self.q, self.p) == 1: + m = y + else: + m = -y % self.p else: - m = plaintext.m + m = plaintext.m ciphertext.alpha = pow(self.g, r, self.p) ciphertext.beta = (m * pow(self.y, r, self.p)) % self.p @@ -192,7 +96,7 @@ def encrypt_return_r(self, plaintext): """ Encrypt a plaintext and return the randomness just generated and used. """ - r = Utils.random_mpz_lt(self.q) + r = random.mpz_lt(self.q) ciphertext = self.encrypt_with_r(plaintext, r) return [ciphertext, r] @@ -207,70 +111,69 @@ def to_dict(self): """ Serialize to dictionary. """ - return {'y' : str(self.y), 'p' : str(self.p), 'g' : str(self.g) , 'q' : str(self.q)} + return {'y': str(self.y), 'p': str(self.p), 'g': str(self.g), 'q': str(self.q)} toJSONDict = to_dict # quick hack FIXME def toJSON(self): - import utils - return utils.to_json(self.toJSONDict()) + return to_json(self.toJSONDict()) - def __mul__(self,other): - if other == 0 or other == 1: - return self + def __mul__(self, other): + if other == 0 or other == 1: + return self - # check p and q - if self.p != other.p or self.q != other.q or self.g != other.g: - raise Exception("incompatible public keys") + # check p and q + if self.p != other.p or self.q != other.q or self.g != other.g: + raise Exception("incompatible public keys") - result = EGPublicKey() - result.p = self.p - result.q = self.q - result.g = self.g - result.y = (self.y * other.y) % result.p - return result + result = EGPublicKey() + result.p = self.p + result.q = self.q + result.g = self.g + result.y = (self.y * other.y) % result.p + return result - def verify_sk_proof(self, dlog_proof, challenge_generator = None): - """ - verify the proof of knowledge of the secret key - g^response = commitment * y^challenge - """ - left_side = pow(self.g, dlog_proof.response, self.p) - right_side = (dlog_proof.commitment * pow(self.y, dlog_proof.challenge, self.p)) % self.p + def verify_sk_proof(self, dlog_proof, challenge_generator=None): + """ + verify the proof of knowledge of the secret key + g^response = commitment * y^challenge + """ + left_side = pow(self.g, dlog_proof.response, self.p) + right_side = (dlog_proof.commitment * pow(self.y, dlog_proof.challenge, self.p)) % self.p - expected_challenge = challenge_generator(dlog_proof.commitment) % self.q + expected_challenge = challenge_generator(dlog_proof.commitment) % self.q - return ((left_side == right_side) and (dlog_proof.challenge == expected_challenge)) + return (left_side == right_side) and (dlog_proof.challenge == expected_challenge) def validate_pk_params(self): - # check primality of p - if not number.isPrime(self.p): - raise Exception("p is not prime.") + # check primality of p + if not number.isPrime(self.p): + raise Exception("p is not prime.") - # check length of p - if not (number.size(self.p) >= 2048): - raise Exception("p of insufficient length. Should be 2048 bits or greater.") + # check length of p + if not (number.size(self.p) >= 2048): + raise Exception("p of insufficient length. Should be 2048 bits or greater.") - # check primality of q - if not number.isPrime(self.q): - raise Exception("q is not prime.") + # check primality of q + if not number.isPrime(self.q): + raise Exception("q is not prime.") - # check length of q - if not (number.size(self.q) >= 256): - raise Exception("q of insufficient length. Should be 256 bits or greater.") + # check length of q + if not (number.size(self.q) >= 256): + raise Exception("q of insufficient length. Should be 256 bits or greater.") - if (pow(self.g,self.q,self.p)!=1): - raise Exception("g does not generate subgroup of order q.") + if pow(self.g, self.q, self.p) != 1: + raise Exception("g does not generate subgroup of order q.") - if not (1 < self.g < self.p-1): - raise Exception("g out of range.") + if not (1 < self.g < self.p - 1): + raise Exception("g out of range.") - if not (1 < self.y < self.p-1): - raise Exception("y out of range.") + if not (1 < self.y < self.p - 1): + raise Exception("y out of range.") - if (pow(self.y,self.q,self.p)!=1): - raise Exception("g does not generate proper group.") + if pow(self.y, self.q, self.p) != 1: + raise Exception("g does not generate proper group.") @classmethod def from_dict(cls, d): @@ -284,14 +187,15 @@ def from_dict(cls, d): pk.q = int(d['q']) try: - pk.validate_pk_params() + pk.validate_pk_params() except Exception as e: - raise + raise e return pk fromJSONDict = from_dict + class EGSecretKey: def __init__(self): self.x = None @@ -317,25 +221,25 @@ def decryption_factor_and_proof(self, ciphertext, challenge_generator=None): return dec_factor, proof - def decrypt(self, ciphertext, dec_factor = None, decode_m=False): + def decrypt(self, ciphertext, dec_factor=None, decode_m=False): """ Decrypt a ciphertext. Optional parameter decides whether to encode the message into the proper subgroup. """ if not dec_factor: dec_factor = self.decryption_factor(ciphertext) - m = (Utils.inverse(dec_factor, self.pk.p) * ciphertext.beta) % self.pk.p + m = (number.inverse(dec_factor, self.pk.p) * ciphertext.beta) % self.pk.p if decode_m: - # get m back from the q-order subgroup - if m < self.pk.q: - y = m - else: - y = -m % self.pk.p + # get m back from the q-order subgroup + if m < self.pk.q: + y = m + else: + y = -m % self.pk.p - return EGPlaintext(y-1, self.pk) + return EGPlaintext(y - 1, self.pk) else: - return EGPlaintext(m, self.pk) + return EGPlaintext(m, self.pk) def prove_decryption(self, ciphertext): """ @@ -350,66 +254,66 @@ def prove_decryption(self, ciphertext): and alpha^t = b * beta/m ^ c """ - m = (Utils.inverse(pow(ciphertext.alpha, self.x, self.pk.p), self.pk.p) * ciphertext.beta) % self.pk.p - beta_over_m = (ciphertext.beta * Utils.inverse(m, self.pk.p)) % self.pk.p + m = (number.inverse(pow(ciphertext.alpha, self.x, self.pk.p), self.pk.p) * ciphertext.beta) % self.pk.p + beta_over_m = (ciphertext.beta * number.inverse(m, self.pk.p)) % self.pk.p # pick a random w - w = Utils.random_mpz_lt(self.pk.q) + w = random.mpz_lt(self.pk.q) a = pow(self.pk.g, w, self.pk.p) b = pow(ciphertext.alpha, w, self.pk.p) - c = int(hashlib.sha1(str(a) + "," + str(b)).hexdigest(),16) + c = int(SHA1.new(bytes(str(a) + "," + str(b), 'utf-8')).hexdigest(), 16) t = (w + self.x * c) % self.pk.q return m, { - 'commitment' : {'A' : str(a), 'B': str(b)}, - 'challenge' : str(c), - 'response' : str(t) - } + 'commitment': {'A': str(a), 'B': str(b)}, + 'challenge': str(c), + 'response': str(t) + } def to_dict(self): - return {'x' : str(self.x), 'public_key' : self.pk.to_dict()} + return {'x': str(self.x), 'public_key': self.pk.to_dict()} toJSONDict = to_dict def prove_sk(self, challenge_generator): - """ - Generate a PoK of the secret key - Prover generates w, a random integer modulo q, and computes commitment = g^w mod p. - Verifier provides challenge modulo q. - Prover computes response = w + x*challenge mod q, where x is the secret key. - """ - w = Utils.random_mpz_lt(self.pk.q) - commitment = pow(self.pk.g, w, self.pk.p) - challenge = challenge_generator(commitment) % self.pk.q - response = (w + (self.x * challenge)) % self.pk.q - - return DLogProof(commitment, challenge, response) + """ + Generate a PoK of the secret key + Prover generates w, a random integer modulo q, and computes commitment = g^w mod p. + Verifier provides challenge modulo q. + Prover computes response = w + x*challenge mod q, where x is the secret key. + """ + w = random.mpz_lt(self.pk.q) + commitment = pow(self.pk.g, w, self.pk.p) + challenge = challenge_generator(commitment) % self.pk.q + response = (w + (self.x * challenge)) % self.pk.q + return DLogProof(commitment, challenge, response) @classmethod def from_dict(cls, d): if not d: - return None + return None sk = cls() sk.x = int(d['x']) - if d.has_key('public_key'): - sk.pk = EGPublicKey.from_dict(d['public_key']) + if 'public_key' in d: + sk.pk = EGPublicKey.from_dict(d['public_key']) else: - sk.pk = None + sk.pk = None return sk fromJSONDict = from_dict + class EGPlaintext: - def __init__(self, m = None, pk = None): + def __init__(self, m=None, pk=None): self.m = m self.pk = pk def to_dict(self): - return {'m' : self.m} + return {'m': self.m} @classmethod def from_dict(cls, d): @@ -424,17 +328,17 @@ def __init__(self, alpha=None, beta=None, pk=None): self.alpha = alpha self.beta = beta - def __mul__(self,other): + def __mul__(self, other): """ Homomorphic Multiplication of ciphertexts. """ - if type(other) == int and (other == 0 or other == 1): - return self + if isinstance(other, int) and (other == 0 or other == 1): + return self if self.pk != other.pk: - logging.info(self.pk) - logging.info(other.pk) - raise Exception('different PKs!') + logging.info(self.pk) + logging.info(other.pk) + raise Exception('different PKs!') new = EGCiphertext() @@ -460,7 +364,7 @@ def reenc_return_r(self): """ Reencryption with fresh randomness, which is returned. """ - r = Utils.random_mpz_lt(self.pk.q) + r = random.mpz_lt(self.pk.q) new_c = self.reenc_with_r(r) return [new_c, r] @@ -471,189 +375,195 @@ def reenc(self): return self.reenc_return_r()[0] def __eq__(self, other): - """ - Check for ciphertext equality. - """ - if other == None: - return False + """ + Check for ciphertext equality. + """ + if other is None: + return False - return (self.alpha == other.alpha and self.beta == other.beta) + return self.alpha == other.alpha and self.beta == other.beta def generate_encryption_proof(self, plaintext, randomness, challenge_generator): - """ - Generate the disjunctive encryption proof of encryption - """ - # random W - w = Utils.random_mpz_lt(self.pk.q) + """ + Generate the disjunctive encryption proof of encryption + """ + # random W + w = random.mpz_lt(self.pk.q) - # build the proof - proof = EGZKProof() + # build the proof + proof = EGZKProof() - # compute A=g^w, B=y^w - proof.commitment['A'] = pow(self.pk.g, w, self.pk.p) - proof.commitment['B'] = pow(self.pk.y, w, self.pk.p) + # compute A=g^w, B=y^w + proof.commitment['A'] = pow(self.pk.g, w, self.pk.p) + proof.commitment['B'] = pow(self.pk.y, w, self.pk.p) - # generate challenge - proof.challenge = challenge_generator(proof.commitment); + # generate challenge + proof.challenge = challenge_generator(proof.commitment) - # Compute response = w + randomness * challenge - proof.response = (w + (randomness * proof.challenge)) % self.pk.q; + # Compute response = w + randomness * challenge + proof.response = (w + (randomness * proof.challenge)) % self.pk.q - return proof; + return proof def simulate_encryption_proof(self, plaintext, challenge=None): - # generate a random challenge if not provided - if not challenge: - challenge = Utils.random_mpz_lt(self.pk.q) + # generate a random challenge if not provided + if not challenge: + challenge = random.mpz_lt(self.pk.q) - proof = EGZKProof() - proof.challenge = challenge + proof = EGZKProof() + proof.challenge = challenge - # compute beta/plaintext, the completion of the DH tuple - beta_over_plaintext = (self.beta * Utils.inverse(plaintext.m, self.pk.p)) % self.pk.p + # compute beta/plaintext, the completion of the DH tuple + beta_over_plaintext = (self.beta * number.inverse(plaintext.m, self.pk.p)) % self.pk.p - # random response, does not even need to depend on the challenge - proof.response = Utils.random_mpz_lt(self.pk.q); + # random response, does not even need to depend on the challenge + proof.response = random.mpz_lt(self.pk.q) - # now we compute A and B - proof.commitment['A'] = (Utils.inverse(pow(self.alpha, proof.challenge, self.pk.p), self.pk.p) * pow(self.pk.g, proof.response, self.pk.p)) % self.pk.p - proof.commitment['B'] = (Utils.inverse(pow(beta_over_plaintext, proof.challenge, self.pk.p), self.pk.p) * pow(self.pk.y, proof.response, self.pk.p)) % self.pk.p + # now we compute A and B + proof.commitment['A'] = (number.inverse(pow(self.alpha, proof.challenge, self.pk.p), self.pk.p) + * pow(self.pk.g, proof.response, self.pk.p) + ) % self.pk.p + proof.commitment['B'] = (number.inverse(pow(beta_over_plaintext, proof.challenge, self.pk.p), self.pk.p) * pow( + self.pk.y, proof.response, self.pk.p)) % self.pk.p - return proof + return proof def generate_disjunctive_encryption_proof(self, plaintexts, real_index, randomness, challenge_generator): - # note how the interface is as such so that the result does not reveal which is the real proof. + # note how the interface is as such so that the result does not reveal which is the real proof. - proofs = [None for p in plaintexts] + proofs = [None for _ in plaintexts] - # go through all plaintexts and simulate the ones that must be simulated. - for p_num in range(len(plaintexts)): - if p_num != real_index: - proofs[p_num] = self.simulate_encryption_proof(plaintexts[p_num]) + # go through all plaintexts and simulate the ones that must be simulated. + for p_num in range(len(plaintexts)): + if p_num != real_index: + proofs[p_num] = self.simulate_encryption_proof(plaintexts[p_num]) - # the function that generates the challenge - def real_challenge_generator(commitment): - # set up the partial real proof so we're ready to get the hash - proofs[real_index] = EGZKProof() - proofs[real_index].commitment = commitment + # the function that generates the challenge + def real_challenge_generator(commitment): + # set up the partial real proof so we're ready to get the hash + proofs[real_index] = EGZKProof() + proofs[real_index].commitment = commitment - # get the commitments in a list and generate the whole disjunctive challenge - commitments = [p.commitment for p in proofs] - disjunctive_challenge = challenge_generator(commitments); + # get the commitments in a list and generate the whole disjunctive challenge + commitments = [p.commitment for p in proofs] + disjunctive_challenge = challenge_generator(commitments) - # now we must subtract all of the other challenges from this challenge. - real_challenge = disjunctive_challenge - for p_num in range(len(proofs)): - if p_num != real_index: - real_challenge = real_challenge - proofs[p_num].challenge + # now we must subtract all of the other challenges from this challenge. + real_challenge = disjunctive_challenge + for p_num in range(len(proofs)): + if p_num != real_index: + real_challenge = real_challenge - proofs[p_num].challenge - # make sure we mod q, the exponent modulus - return real_challenge % self.pk.q + # make sure we mod q, the exponent modulus + return real_challenge % self.pk.q - # do the real proof - real_proof = self.generate_encryption_proof(plaintexts[real_index], randomness, real_challenge_generator) + # do the real proof + real_proof = self.generate_encryption_proof(plaintexts[real_index], randomness, real_challenge_generator) - # set the real proof - proofs[real_index] = real_proof + # set the real proof + proofs[real_index] = real_proof - return EGZKDisjunctiveProof(proofs) + return EGZKDisjunctiveProof(proofs) def verify_encryption_proof(self, plaintext, proof): - """ - Checks for the DDH tuple g, y, alpha, beta/plaintext. - (PoK of randomness r.) - - Proof contains commitment = {A, B}, challenge, response - """ - # check that A, B are in the correct group - if not (pow(proof.commitment['A'],self.pk.q,self.pk.p)==1 and pow(proof.commitment['B'],self.pk.q,self.pk.p)==1): - return False + """ + Checks for the DDH tuple g, y, alpha, beta/plaintext. + (PoK of randomness r.) + + Proof contains commitment = {A, B}, challenge, response + """ + # check that A, B are in the correct group + if not (pow(proof.commitment['A'], self.pk.q, self.pk.p) == 1 and pow(proof.commitment['B'], self.pk.q, + self.pk.p) == 1): + return False - # check that g^response = A * alpha^challenge - first_check = (pow(self.pk.g, proof.response, self.pk.p) == ((pow(self.alpha, proof.challenge, self.pk.p) * proof.commitment['A']) % self.pk.p)) + # check that g^response = A * alpha^challenge + first_check = (pow(self.pk.g, proof.response, self.pk.p) == ( + (pow(self.alpha, proof.challenge, self.pk.p) * proof.commitment['A']) % self.pk.p)) - # check that y^response = B * (beta/m)^challenge - beta_over_m = (self.beta * Utils.inverse(plaintext.m, self.pk.p)) % self.pk.p - second_check = (pow(self.pk.y, proof.response, self.pk.p) == ((pow(beta_over_m, proof.challenge, self.pk.p) * proof.commitment['B']) % self.pk.p)) + # check that y^response = B * (beta/m)^challenge + beta_over_m = (self.beta * number.inverse(plaintext.m, self.pk.p)) % self.pk.p + second_check = (pow(self.pk.y, proof.response, self.pk.p) == ( + (pow(beta_over_m, proof.challenge, self.pk.p) * proof.commitment['B']) % self.pk.p)) - # print "1,2: %s %s " % (first_check, second_check) - return (first_check and second_check) + # print "1,2: %s %s " % (first_check, second_check) + return first_check and second_check def verify_disjunctive_encryption_proof(self, plaintexts, proof, challenge_generator): - """ - plaintexts and proofs are all lists of equal length, with matching. + """ + plaintexts and proofs are all lists of equal length, with matching. - overall_challenge is what all of the challenges combined should yield. - """ - if len(plaintexts) != len(proof.proofs): - print("bad number of proofs (expected %s, found %s)" % (len(plaintexts), len(proof.proofs))) - return False + overall_challenge is what all of the challenges combined should yield. + """ + if len(plaintexts) != len(proof.proofs): + print("bad number of proofs (expected %s, found %s)" % (len(plaintexts), len(proof.proofs))) + return False - for i in range(len(plaintexts)): - # if a proof fails, stop right there - if not self.verify_encryption_proof(plaintexts[i], proof.proofs[i]): - print "bad proof %s, %s, %s" % (i, plaintexts[i], proof.proofs[i]) - return False + for i in range(len(plaintexts)): + # if a proof fails, stop right there + if not self.verify_encryption_proof(plaintexts[i], proof.proofs[i]): + print("bad proof %s, %s, %s" % (i, plaintexts[i], proof.proofs[i])) + return False - # logging.info("made it past the two encryption proofs") + # logging.info("made it past the two encryption proofs") - # check the overall challenge - return (challenge_generator([p.commitment for p in proof.proofs]) == (sum([p.challenge for p in proof.proofs]) % self.pk.q)) + # check the overall challenge + return (challenge_generator([p.commitment for p in proof.proofs]) == ( + sum([p.challenge for p in proof.proofs]) % self.pk.q)) def verify_decryption_proof(self, plaintext, proof): - """ - Checks for the DDH tuple g, alpha, y, beta/plaintext - (PoK of secret key x.) - """ - return False + """ + Checks for the DDH tuple g, alpha, y, beta/plaintext + (PoK of secret key x.) + """ + return False def verify_decryption_factor(self, dec_factor, dec_proof, public_key): - """ - when a ciphertext is decrypted by a dec factor, the proof needs to be checked - """ - pass + """ + when a ciphertext is decrypted by a dec factor, the proof needs to be checked + """ + pass def decrypt(self, decryption_factors, public_key): - """ - decrypt a ciphertext given a list of decryption factors (from multiple trustees) - For now, no support for threshold - """ - running_decryption = self.beta - for dec_factor in decryption_factors: - running_decryption = (running_decryption * Utils.inverse(dec_factor, public_key.p)) % public_key.p + """ + decrypt a ciphertext given a list of decryption factors (from multiple trustees) + For now, no support for threshold + """ + running_decryption = self.beta + for dec_factor in decryption_factors: + running_decryption = (running_decryption * number.inverse(dec_factor, public_key.p)) % public_key.p - return running_decryption + return running_decryption def check_group_membership(self, pk): - """ - checks to see if an ElGamal element belongs to the group in the pk - """ - if not (1 < self.alpha < pk.p-1): - return False - - elif not (1 < self.beta < pk.p-1): - return False + """ + checks to see if an ElGamal element belongs to the group in the pk + """ + if not (1 < self.alpha < pk.p - 1): + return False - elif (pow(self.alpha, pk.q, pk.p)!=1): - return False + elif not (1 < self.beta < pk.p - 1): + return False - elif (pow(self.beta, pk.q, pk.p)!=1): - return False + elif pow(self.alpha, pk.q, pk.p) != 1: + return False - else: - return True + elif pow(self.beta, pk.q, pk.p) != 1: + return False + else: + return True def to_dict(self): return {'alpha': str(self.alpha), 'beta': str(self.beta)} - toJSONDict= to_dict + toJSONDict = to_dict def to_string(self): return "%s,%s" % (self.alpha, self.beta) @classmethod - def from_dict(cls, d, pk = None): + def from_dict(cls, d, pk=None): result = cls() result.alpha = int(d['alpha']) result.beta = int(d['beta']) @@ -668,127 +578,134 @@ def from_string(cls, str): expects alpha,beta """ split = str.split(",") - return cls.from_dict({'alpha' : split[0], 'beta' : split[1]}) + return cls.from_dict({'alpha': split[0], 'beta': split[1]}) + class EGZKProof(object): - def __init__(self): - self.commitment = {'A':None, 'B':None} - self.challenge = None - self.response = None + def __init__(self): + self.commitment = {'A': None, 'B': None} + self.challenge = None + self.response = None - @classmethod - def generate(cls, little_g, little_h, x, p, q, challenge_generator): - """ - generate a DDH tuple proof, where challenge generator is - almost certainly EG_fiatshamir_challenge_generator - """ + @classmethod + def generate(cls, little_g, little_h, x, p, q, challenge_generator): + """ + generate a DDH tuple proof, where challenge generator is + almost certainly EG_fiatshamir_challenge_generator + """ - # generate random w - w = Utils.random_mpz_lt(q) + # generate random w + w = random.mpz_lt(q) - # create proof instance - proof = cls() + # create proof instance + proof = cls() - # compute A = little_g^w, B=little_h^w - proof.commitment['A'] = pow(little_g, w, p) - proof.commitment['B'] = pow(little_h, w, p) + # compute A = little_g^w, B=little_h^w + proof.commitment['A'] = pow(little_g, w, p) + proof.commitment['B'] = pow(little_h, w, p) - # get challenge - proof.challenge = challenge_generator(proof.commitment) + # get challenge + proof.challenge = challenge_generator(proof.commitment) - # compute response - proof.response = (w + (x * proof.challenge)) % q + # compute response + proof.response = (w + (x * proof.challenge)) % q - # return proof - return proof + # return proof + return proof - @classmethod - def from_dict(cls, d): - p = cls() - p.commitment = {'A': int(d['commitment']['A']), 'B': int(d['commitment']['B'])} - p.challenge = int(d['challenge']) - p.response = int(d['response']) - return p + @classmethod + def from_dict(cls, d): + p = cls() + p.commitment = {'A': int(d['commitment']['A']), 'B': int(d['commitment']['B'])} + p.challenge = int(d['challenge']) + p.response = int(d['response']) + return p - fromJSONDict = from_dict + fromJSONDict = from_dict + + def to_dict(self): + return { + 'commitment': {'A': str(self.commitment['A']), 'B': str(self.commitment['B'])}, + 'challenge': str(self.challenge), + 'response': str(self.response) + } - def to_dict(self): - return { - 'commitment' : {'A' : str(self.commitment['A']), 'B' : str(self.commitment['B'])}, - 'challenge': str(self.challenge), - 'response': str(self.response) - } + toJSONDict = to_dict - def verify(self, little_g, little_h, big_g, big_h, p, q, challenge_generator=None): - """ - Verify a DH tuple proof - """ - # check that A, B are in the correct group - if not (pow(proof.commitment['A'],self.pk.q,self.pk.p)==1 and pow(proof.commitment['B'],self.pk.q,self.pk.p)==1): - return False + def verify(self, little_g, little_h, big_g, big_h, p, q, challenge_generator=None): + """ + Verify a DH tuple proof + """ + # check that A, B are in the correct group + if not (pow(self.commitment['A'], self.pk.q, self.pk.p) == 1 + and pow(self.commitment['B'], self.pk.q, self.pk.p) == 1): + return False - # check that little_g^response = A * big_g^challenge - first_check = (pow(little_g, self.response, p) == ((pow(big_g, self.challenge, p) * self.commitment['A']) % p)) + # check that little_g^response = A * big_g^challenge + first_check = (pow(little_g, self.response, p) == ((pow(big_g, self.challenge, p) * self.commitment['A']) % p)) - # check that little_h^response = B * big_h^challenge - second_check = (pow(little_h, self.response, p) == ((pow(big_h, self.challenge, p) * self.commitment['B']) % p)) + # check that little_h^response = B * big_h^challenge + second_check = (pow(little_h, self.response, p) == ((pow(big_h, self.challenge, p) * self.commitment['B']) % p)) - # check the challenge? - third_check = True + # check the challenge? + third_check = True - if challenge_generator: - third_check = (self.challenge == challenge_generator(self.commitment)) + if challenge_generator: + third_check = (self.challenge == challenge_generator(self.commitment)) - return (first_check and second_check and third_check) + return first_check and second_check and third_check - toJSONDict = to_dict class EGZKDisjunctiveProof: - def __init__(self, proofs = None): - self.proofs = proofs + def __init__(self, proofs=None): + self.proofs = proofs - @classmethod - def from_dict(cls, d): - dp = cls() - dp.proofs = [EGZKProof.from_dict(p) for p in d] - return dp + @classmethod + def from_dict(cls, d): + dp = cls() + dp.proofs = [EGZKProof.from_dict(p) for p in d] + return dp - def to_dict(self): - return [p.to_dict() for p in self.proofs] + def to_dict(self): + return [p.to_dict() for p in self.proofs] + + toJSONDict = to_dict - toJSONDict = to_dict class DLogProof(object): - def __init__(self, commitment, challenge, response): - self.commitment = commitment - self.challenge = challenge - self.response = response + def __init__(self, commitment, challenge, response): + self.commitment = commitment + self.challenge = challenge + self.response = response - def to_dict(self): - return {'challenge': str(self.challenge), 'commitment': str(self.commitment), 'response' : str(self.response)} + def to_dict(self): + return {'challenge': str(self.challenge), 'commitment': str(self.commitment), 'response': str(self.response)} - toJSONDict = to_dict + toJSONDict = to_dict - @classmethod - def from_dict(cls, d): - dlp = cls(int(d['commitment']), int(d['challenge']), int(d['response'])) - return dlp + @classmethod + def from_dict(cls, d): + dlp = cls(int(d['commitment']), int(d['challenge']), int(d['response'])) + return dlp + + fromJSONDict = from_dict - fromJSONDict = from_dict def EG_disjunctive_challenge_generator(commitments): - array_to_hash = [] - for commitment in commitments: - array_to_hash.append(str(commitment['A'])) - array_to_hash.append(str(commitment['B'])) + array_to_hash = [] + for commitment in commitments: + array_to_hash.append(str(commitment['A'])) + array_to_hash.append(str(commitment['B'])) + + string_to_hash = ",".join(array_to_hash) + return int(SHA1.new(bytes(string_to_hash, 'utf-8')).hexdigest(), 16) - string_to_hash = ",".join(array_to_hash) - return int(hashlib.sha1(string_to_hash).hexdigest(),16) # a challenge generator for Fiat-Shamir with A,B commitment def EG_fiatshamir_challenge_generator(commitment): - return EG_disjunctive_challenge_generator([commitment]) + return EG_disjunctive_challenge_generator([commitment]) + def DLog_challenge_generator(commitment): - string_to_hash = str(commitment) - return int(hashlib.sha1(string_to_hash).hexdigest(),16) + string_to_hash = str(commitment) + return int(SHA1.new(bytes(string_to_hash, 'utf-8')).hexdigest(), 16) diff --git a/helios/crypto/electionalgs.py b/helios/crypto/electionalgs.py index c67714043..de833f8da 100644 --- a/helios/crypto/electionalgs.py +++ b/helios/crypto/electionalgs.py @@ -4,778 +4,807 @@ Ben Adida 2008-08-30 """ - -import algs -import logging -import utils -import uuid import datetime +import uuid +import logging -class HeliosObject(object): - """ - A base class to ease serialization and de-serialization - crypto objects are kept as full-blown crypto objects, serialized to jsonobjects on the way out - and deserialized from jsonobjects on the way in - """ - FIELDS = [] - JSON_FIELDS = None - - def __init__(self, **kwargs): - self.set_from_args(**kwargs) - - # generate uuid if need be - if 'uuid' in self.FIELDS and (not hasattr(self, 'uuid') or self.uuid == None): - self.uuid = str(uuid.uuid4()) - - def set_from_args(self, **kwargs): - for f in self.FIELDS: - if kwargs.has_key(f): - new_val = self.process_value_in(f, kwargs[f]) - setattr(self, f, new_val) - else: - setattr(self, f, None) - - def set_from_other_object(self, o): - for f in self.FIELDS: - if hasattr(o, f): - setattr(self, f, self.process_value_in(f, getattr(o,f))) - else: - setattr(self, f, None) - - def toJSON(self): - return utils.to_json(self.toJSONDict()) - - def toJSONDict(self, alternate_fields=None): - val = {} - for f in (alternate_fields or self.JSON_FIELDS or self.FIELDS): - val[f] = self.process_value_out(f, getattr(self, f)) - return val - - @classmethod - def fromJSONDict(cls, d): - # go through the keys and fix them - new_d = {} - for k in d.keys(): - new_d[str(k)] = d[k] - - return cls(**new_d) - - @classmethod - def fromOtherObject(cls, o): - obj = cls() - obj.set_from_other_object(o) - return obj - - def toOtherObject(self, o): - for f in self.FIELDS: - # FIXME: why isn't this working? - if hasattr(o, f): - # BIG HAMMER - try: - setattr(o, f, self.process_value_out(f, getattr(self,f))) - except: - pass - - @property - def hash(self): - s = utils.to_json(self.toJSONDict()) - return utils.hash_b64(s) - - def process_value_in(self, field_name, field_value): - """ - process some fields on the way into the object - """ - if field_value == None: - return None - - val = self._process_value_in(field_name, field_value) - if val != None: - return val - else: - return field_value +from helios.utils import to_json +from . import algs +from . import utils - def _process_value_in(self, field_name, field_value): - return None - def process_value_out(self, field_name, field_value): +class HeliosObject(object): """ - process some fields on the way out of the object + A base class to ease serialization and de-serialization + crypto objects are kept as full-blown crypto objects, serialized to jsonobjects on the way out + and deserialized from jsonobjects on the way in """ - if field_value == None: - return None - - val = self._process_value_out(field_name, field_value) - if val != None: - return val - else: - return field_value - - def _process_value_out(self, field_name, field_value): - return None + FIELDS = [] + JSON_FIELDS = None + + def __init__(self, **kwargs): + self.set_from_args(**kwargs) + + # generate uuid if need be + if 'uuid' in self.FIELDS and (not hasattr(self, 'uuid') or self.uuid is None): + self.uuid = str(uuid.uuid4()) + + def set_from_args(self, **kwargs): + for f in self.FIELDS: + if f in kwargs: + new_val = self.process_value_in(f, kwargs[f]) + setattr(self, f, new_val) + else: + setattr(self, f, None) + + def set_from_other_object(self, o): + for f in self.FIELDS: + if hasattr(o, f): + setattr(self, f, self.process_value_in(f, getattr(o, f))) + else: + setattr(self, f, None) + + def toJSON(self): + return to_json(self.toJSONDict()) + + def toJSONDict(self, alternate_fields=None): + val = {} + for f in (alternate_fields or self.JSON_FIELDS or self.FIELDS): + val[f] = self.process_value_out(f, getattr(self, f)) + return val + + @classmethod + def fromJSONDict(cls, d): + # go through the keys and fix them + new_d = {} + for k in list(d.keys()): + new_d[str(k)] = d[k] + + return cls(**new_d) + + @classmethod + def fromOtherObject(cls, o): + obj = cls() + obj.set_from_other_object(o) + return obj + + def toOtherObject(self, o): + for f in self.FIELDS: + # FIXME: why isn't this working? + if hasattr(o, f): + # BIG HAMMER + try: + setattr(o, f, self.process_value_out(f, getattr(self, f))) + except: + pass + + @property + def hash(self): + s = to_json(self.toJSONDict()) + return utils.hash_b64(s) + + def process_value_in(self, field_name, field_value): + """ + process some fields on the way into the object + """ + if field_value is None: + return None + + val = self._process_value_in(field_name, field_value) + if val is not None: + return val + else: + return field_value + + def _process_value_in(self, field_name, field_value): + return None + + def process_value_out(self, field_name, field_value): + """ + process some fields on the way out of the object + """ + if field_value is None: + return None + + val = self._process_value_out(field_name, field_value) + if val is not None: + return val + else: + return field_value + + def _process_value_out(self, field_name, field_value): + return None + + def __eq__(self, other): + if not hasattr(self, 'uuid'): + return super(HeliosObject, self) == other + + return other is not None and self.uuid == other.uuid - def __eq__(self, other): - if not hasattr(self, 'uuid'): - return super(HeliosObject,self) == other - - return other != None and self.uuid == other.uuid class EncryptedAnswer(HeliosObject): - """ - An encrypted answer to a single election question - """ - - FIELDS = ['choices', 'individual_proofs', 'overall_proof', 'randomness', 'answer'] - - # FIXME: remove this constructor and use only named-var constructor from HeliosObject - def __init__(self, choices=None, individual_proofs=None, overall_proof=None, randomness=None, answer=None): - self.choices = choices - self.individual_proofs = individual_proofs - self.overall_proof = overall_proof - self.randomness = randomness - self.answer = answer - - @classmethod - def generate_plaintexts(cls, pk, min=0, max=1): - plaintexts = [] - running_product = 1 - - # run the product up to the min - for i in range(max+1): - # if we're in the range, add it to the array - if i >= min: - plaintexts.append(algs.EGPlaintext(running_product, pk)) - - # next value in running product - running_product = (running_product * pk.g) % pk.p - - return plaintexts - - def verify_plaintexts_and_randomness(self, pk): """ - this applies only if the explicit answers and randomness factors are given - we do not verify the proofs here, that is the verify() method + An encrypted answer to a single election question """ - if not hasattr(self, 'answer'): - return False - - for choice_num in range(len(self.choices)): - choice = self.choices[choice_num] - choice.pk = pk - # redo the encryption - # WORK HERE (paste from below encryption) + FIELDS = ['choices', 'individual_proofs', 'overall_proof', 'randomness', 'answer'] - return False + # FIXME: remove this constructor and use only named-var constructor from HeliosObject + def __init__(self, choices=None, individual_proofs=None, overall_proof=None, randomness=None, answer=None): + self.choices = choices + self.individual_proofs = individual_proofs + self.overall_proof = overall_proof + self.randomness = randomness + self.answer = answer - def verify(self, pk, min=0, max=1): - possible_plaintexts = self.generate_plaintexts(pk) - homomorphic_sum = 0 + @classmethod + def generate_plaintexts(cls, pk, min=0, max=1): + plaintexts = [] + running_product = 1 - for choice_num in range(len(self.choices)): - choice = self.choices[choice_num] - choice.pk = pk - individual_proof = self.individual_proofs[choice_num] + # run the product up to the min + for i in range(max + 1): + # if we're in the range, add it to the array + if i >= min: + plaintexts.append(algs.EGPlaintext(running_product, pk)) - # verify that elements belong to the proper group - if not choice.check_group_membership(pk): - return False - - # verify the proof on the encryption of that choice - if not choice.verify_disjunctive_encryption_proof(possible_plaintexts, individual_proof, algs.EG_disjunctive_challenge_generator): - return False + # next value in running product + running_product = (running_product * pk.g) % pk.p - # compute homomorphic sum if needed - if max != None: - homomorphic_sum = choice * homomorphic_sum + return plaintexts - if max != None: - # determine possible plaintexts for the sum - sum_possible_plaintexts = self.generate_plaintexts(pk, min=min, max=max) + def verify_plaintexts_and_randomness(self, pk): + """ + this applies only if the explicit answers and randomness factors are given + we do not verify the proofs here, that is the verify() method + """ + if not hasattr(self, 'answer'): + return False - # verify the sum - return homomorphic_sum.verify_disjunctive_encryption_proof(sum_possible_plaintexts, self.overall_proof, algs.EG_disjunctive_challenge_generator) - else: - # approval voting, no need for overall proof verification - return True + for choice_num in range(len(self.choices)): + choice = self.choices[choice_num] + choice.pk = pk - def toJSONDict(self, with_randomness=False): - value = { - 'choices': [c.to_dict() for c in self.choices], - 'individual_proofs' : [p.to_dict() for p in self.individual_proofs] - } + # redo the encryption + # WORK HERE (paste from below encryption) - if self.overall_proof: - value['overall_proof'] = self.overall_proof.to_dict() - else: - value['overall_proof'] = None - - if with_randomness: - value['randomness'] = [str(r) for r in self.randomness] - value['answer'] = self.answer - - return value - - @classmethod - def fromJSONDict(cls, d, pk=None): - ea = cls() - - ea.choices = [algs.EGCiphertext.from_dict(c, pk) for c in d['choices']] - ea.individual_proofs = [algs.EGZKDisjunctiveProof.from_dict(p) for p in d['individual_proofs']] - - if d['overall_proof']: - ea.overall_proof = algs.EGZKDisjunctiveProof.from_dict(d['overall_proof']) - else: - ea.overall_proof = None + return False - if d.has_key('randomness'): - ea.randomness = [int(r) for r in d['randomness']] - ea.answer = d['answer'] + def verify(self, pk, min=0, max=1): + possible_plaintexts = self.generate_plaintexts(pk) + homomorphic_sum = 0 + + for choice_num in range(len(self.choices)): + choice = self.choices[choice_num] + choice.pk = pk + individual_proof = self.individual_proofs[choice_num] + + # verify that elements belong to the proper group + if not choice.check_group_membership(pk): + return False + + # verify the proof on the encryption of that choice + if not choice.verify_disjunctive_encryption_proof(possible_plaintexts, individual_proof, + algs.EG_disjunctive_challenge_generator): + return False + + # compute homomorphic sum if needed + if max is not None: + homomorphic_sum = choice * homomorphic_sum + + if max is not None: + # determine possible plaintexts for the sum + sum_possible_plaintexts = self.generate_plaintexts(pk, min=min, max=max) + + # verify the sum + return homomorphic_sum.verify_disjunctive_encryption_proof(sum_possible_plaintexts, self.overall_proof, + algs.EG_disjunctive_challenge_generator) + else: + # approval voting, no need for overall proof verification + return True + + def toJSONDict(self, with_randomness=False): + value = { + 'choices': [c.to_dict() for c in self.choices], + 'individual_proofs': [p.to_dict() for p in self.individual_proofs] + } + + if self.overall_proof: + value['overall_proof'] = self.overall_proof.to_dict() + else: + value['overall_proof'] = None + + if with_randomness: + value['randomness'] = [str(r) for r in self.randomness] + value['answer'] = self.answer + + return value + + @classmethod + def fromJSONDict(cls, d, pk=None): + ea = cls() + + ea.choices = [algs.EGCiphertext.from_dict(c, pk) for c in d['choices']] + ea.individual_proofs = [algs.EGZKDisjunctiveProof.from_dict(p) for p in d['individual_proofs']] + + if d['overall_proof']: + ea.overall_proof = algs.EGZKDisjunctiveProof.from_dict(d['overall_proof']) + else: + ea.overall_proof = None + + if 'randomness' in d: + ea.randomness = [int(r) for r in d['randomness']] + ea.answer = d['answer'] + + return ea + + @classmethod + def fromElectionAndAnswer(cls, election, question_num, answer_indexes): + """ + Given an election, a question number, and a list of answers to that question + in the form of an array of 0-based indexes into the answer array, + produce an EncryptedAnswer that works. + """ + question = election.questions[question_num] + answers = question['answers'] + pk = election.public_key + + # initialize choices, individual proofs, randomness and overall proof + choices = [None for _ in range(len(answers))] + individual_proofs = [None for _ in range(len(answers))] + randomness = [None for _ in range(len(answers))] + + # possible plaintexts [0, 1] + plaintexts = cls.generate_plaintexts(pk) + + # keep track of number of options selected. + num_selected_answers = 0 + + # homomorphic sum of all + homomorphic_sum = 0 + randomness_sum = 0 + + # min and max for number of answers, useful later + min_answers = 0 + if 'min' in question: + min_answers = question['min'] + max_answers = question['max'] + + # go through each possible answer and encrypt either a g^0 or a g^1. + for answer_num in range(len(answers)): + plaintext_index = 0 + + # assuming a list of answers + if answer_num in answer_indexes: + plaintext_index = 1 + num_selected_answers += 1 + + # randomness and encryption + randomness[answer_num] = utils.random.mpz_lt(pk.q) + choices[answer_num] = pk.encrypt_with_r(plaintexts[plaintext_index], randomness[answer_num]) + + # generate proof + individual_proofs[answer_num] = choices[answer_num].generate_disjunctive_encryption_proof(plaintexts, + plaintext_index, + randomness[ + answer_num], + algs.EG_disjunctive_challenge_generator) + + # sum things up homomorphically if needed + if max_answers is not None: + homomorphic_sum = choices[answer_num] * homomorphic_sum + randomness_sum = (randomness_sum + randomness[answer_num]) % pk.q + + # prove that the sum is 0 or 1 (can be "blank vote" for this answer) + # num_selected_answers is 0 or 1, which is the index into the plaintext that is actually encoded + + if num_selected_answers < min_answers: + raise Exception("Need to select at least %s answer(s)" % min_answers) + + if max_answers is not None: + sum_plaintexts = cls.generate_plaintexts(pk, min=min_answers, max=max_answers) + + # need to subtract the min from the offset + overall_proof = homomorphic_sum.generate_disjunctive_encryption_proof(sum_plaintexts, + num_selected_answers - min_answers, + randomness_sum, + algs.EG_disjunctive_challenge_generator); + else: + # approval voting + overall_proof = None + + return cls(choices, individual_proofs, overall_proof, randomness, answer_indexes) - return ea - @classmethod - def fromElectionAndAnswer(cls, election, question_num, answer_indexes): +class EncryptedVote(HeliosObject): """ - Given an election, a question number, and a list of answers to that question - in the form of an array of 0-based indexes into the answer array, - produce an EncryptedAnswer that works. + An encrypted ballot """ - question = election.questions[question_num] - answers = question['answers'] - pk = election.public_key - - # initialize choices, individual proofs, randomness and overall proof - choices = [None for a in range(len(answers))] - individual_proofs = [None for a in range(len(answers))] - overall_proof = None - randomness = [None for a in range(len(answers))] - - # possible plaintexts [0, 1] - plaintexts = cls.generate_plaintexts(pk) - - # keep track of number of options selected. - num_selected_answers = 0; - - # homomorphic sum of all - homomorphic_sum = 0 - randomness_sum = 0 - - # min and max for number of answers, useful later - min_answers = 0 - if question.has_key('min'): - min_answers = question['min'] - max_answers = question['max'] + FIELDS = ['encrypted_answers', 'election_hash', 'election_uuid'] + + def verify(self, election): + # correct number of answers + # noinspection PyUnresolvedReferences + n_answers = len(self.encrypted_answers) if self.encrypted_answers is not None else 0 + n_questions = len(election.questions) if election.questions is not None else 0 + if n_answers != n_questions: + logging.error(f"Incorrect number of answers ({n_answers}) vs questions ({n_questions})") + return False + + # check hash + # noinspection PyUnresolvedReferences + our_election_hash = self.election_hash if isinstance(self.election_hash, str) else self.election_hash.decode() + actual_election_hash = election.hash if isinstance(election.hash, str) else election.hash.decode() + if our_election_hash != actual_election_hash: + logging.error(f"Incorrect election_hash {our_election_hash} vs {actual_election_hash} ") + return False + + # check ID + # noinspection PyUnresolvedReferences + our_election_uuid = self.election_uuid if isinstance(self.election_uuid, str) else self.election_uuid.decode() + actual_election_uuid = election.uuid if isinstance(election.uuid, str) else election.uuid.decode() + if our_election_uuid != actual_election_uuid: + logging.error(f"Incorrect election_uuid {our_election_uuid} vs {actual_election_uuid} ") + return False + + # check proofs on all of answers + for question_num in range(len(election.questions)): + ea = self.encrypted_answers[question_num] + + question = election.questions[question_num] + min_answers = 0 + if 'min' in question: + min_answers = question['min'] + + if not ea.verify(election.public_key, min=min_answers, max=question['max']): + return False + + return True + + def get_hash(self): + return utils.hash_b64(to_json(self.toJSONDict())) + + def toJSONDict(self, with_randomness=False): + return { + 'answers': [a.toJSONDict(with_randomness) for a in self.encrypted_answers], + 'election_hash': self.election_hash, + 'election_uuid': self.election_uuid + } + + @classmethod + def fromJSONDict(cls, d, pk=None): + ev = cls() + + ev.encrypted_answers = [EncryptedAnswer.fromJSONDict(ea, pk) for ea in d['answers']] + ev.election_hash = d['election_hash'] + ev.election_uuid = d['election_uuid'] + + return ev + + @classmethod + def fromElectionAndAnswers(cls, election, answers): + pk = election.public_key + + # each answer is an index into the answer array + encrypted_answers = [EncryptedAnswer.fromElectionAndAnswer(election, answer_num, answers[answer_num]) for + answer_num in range(len(answers))] + return cls(encrypted_answers=encrypted_answers, election_hash=election.hash, election_uuid=election.uuid) - # go through each possible answer and encrypt either a g^0 or a g^1. - for answer_num in range(len(answers)): - plaintext_index = 0 - # assuming a list of answers - if answer_num in answer_indexes: - plaintext_index = 1 - num_selected_answers += 1 - - # randomness and encryption - randomness[answer_num] = algs.Utils.random_mpz_lt(pk.q) - choices[answer_num] = pk.encrypt_with_r(plaintexts[plaintext_index], randomness[answer_num]) - - # generate proof - individual_proofs[answer_num] = choices[answer_num].generate_disjunctive_encryption_proof(plaintexts, plaintext_index, - randomness[answer_num], algs.EG_disjunctive_challenge_generator) - - # sum things up homomorphically if needed - if max_answers != None: - homomorphic_sum = choices[answer_num] * homomorphic_sum - randomness_sum = (randomness_sum + randomness[answer_num]) % pk.q - - # prove that the sum is 0 or 1 (can be "blank vote" for this answer) - # num_selected_answers is 0 or 1, which is the index into the plaintext that is actually encoded - - if num_selected_answers < min_answers: - raise Exception("Need to select at least %s answer(s)" % min_answers) - - if max_answers != None: - sum_plaintexts = cls.generate_plaintexts(pk, min=min_answers, max=max_answers) +def one_question_winner(question, result, num_cast_votes): + """ + determining the winner for one question + """ + # sort the answers , keep track of the index + counts = sorted(enumerate(result), key=lambda x: x[1]) + counts.reverse() - # need to subtract the min from the offset - overall_proof = homomorphic_sum.generate_disjunctive_encryption_proof(sum_plaintexts, num_selected_answers - min_answers, randomness_sum, algs.EG_disjunctive_challenge_generator); - else: - # approval voting - overall_proof = None + # if there's a max > 1, we assume that the top MAX win + if question['max'] > 1: + return [c[0] for c in counts[:question['max']]] - return cls(choices, individual_proofs, overall_proof, randomness, answer_indexes) + # if max = 1, then depends on absolute or relative + if question['result_type'] == 'absolute': + if counts[0][1] >= (num_cast_votes / 2 + 1): + return [counts[0][0]] + else: + return [] -class EncryptedVote(HeliosObject): - """ - An encrypted ballot - """ - FIELDS = ['encrypted_answers', 'election_hash', 'election_uuid'] - - def verify(self, election): - # right number of answers - if len(self.encrypted_answers) != len(election.questions): - return False - - # check hash - if self.election_hash != election.hash: - # print "%s / %s " % (self.election_hash, election.hash) - return False - - # check ID - if self.election_uuid != election.uuid: - return False - - # check proofs on all of answers - for question_num in range(len(election.questions)): - ea = self.encrypted_answers[question_num] - - question = election.questions[question_num] - min_answers = 0 - if question.has_key('min'): - min_answers = question['min'] - - if not ea.verify(election.public_key, min=min_answers, max=question['max']): - return False + if question['result_type'] == 'relative': + return [counts[0][0]] - return True - - def get_hash(self): - return utils.hash_b64(utils.to_json(self.toJSONDict())) - - def toJSONDict(self, with_randomness=False): - return { - 'answers': [a.toJSONDict(with_randomness) for a in self.encrypted_answers], - 'election_hash': self.election_hash, - 'election_uuid': self.election_uuid - } + +class Election(HeliosObject): + FIELDS = ['uuid', 'questions', 'name', 'short_name', 'description', 'voters_hash', 'openreg', + 'frozen_at', 'public_key', 'private_key', 'cast_url', 'result', 'result_proof', 'use_voter_aliases', + 'voting_starts_at', 'voting_ends_at', 'election_type'] - @classmethod - def fromJSONDict(cls, d, pk=None): - ev = cls() + JSON_FIELDS = ['uuid', 'questions', 'name', 'short_name', 'description', 'voters_hash', 'openreg', + 'frozen_at', 'public_key', 'cast_url', 'use_voter_aliases', 'voting_starts_at', 'voting_ends_at'] - ev.encrypted_answers = [EncryptedAnswer.fromJSONDict(ea, pk) for ea in d['answers']] - ev.election_hash = d['election_hash'] - ev.election_uuid = d['election_uuid'] + # need to add in v3.1: use_advanced_audit_features, election_type, and probably more - return ev + def init_tally(self): + return Tally(election=self) - @classmethod - def fromElectionAndAnswers(cls, election, answers): - pk = election.public_key + def _process_value_in(self, field_name, field_value): + if field_name == 'frozen_at' or field_name == 'voting_starts_at' or field_name == 'voting_ends_at': + if isinstance(field_value, str): + return datetime.datetime.strptime(field_value, '%Y-%m-%d %H:%M:%S') - # each answer is an index into the answer array - encrypted_answers = [EncryptedAnswer.fromElectionAndAnswer(election, answer_num, answers[answer_num]) for answer_num in range(len(answers))] - return cls(encrypted_answers=encrypted_answers, election_hash=election.hash, election_uuid = election.uuid) + if field_name == 'public_key': + return algs.EGPublicKey.fromJSONDict(field_value) + if field_name == 'private_key': + return algs.EGSecretKey.fromJSONDict(field_value) -def one_question_winner(question, result, num_cast_votes): - """ - determining the winner for one question - """ - # sort the answers , keep track of the index - counts = sorted(enumerate(result), key=lambda(x): x[1]) - counts.reverse() - - # if there's a max > 1, we assume that the top MAX win - if question['max'] > 1: - return [c[0] for c in counts[:question['max']]] - - # if max = 1, then depends on absolute or relative - if question['result_type'] == 'absolute': - if counts[0][1] >= (num_cast_votes/2 + 1): - return [counts[0][0]] - else: - return [] - - if question['result_type'] == 'relative': - return [counts[0][0]] + def _process_value_out(self, field_name, field_value): + # the date + if field_name == 'frozen_at' or field_name == 'voting_starts_at' or field_name == 'voting_ends_at': + return str(field_value) -class Election(HeliosObject): + if field_name == 'public_key' or field_name == 'private_key': + return field_value.toJSONDict() - FIELDS = ['uuid', 'questions', 'name', 'short_name', 'description', 'voters_hash', 'openreg', - 'frozen_at', 'public_key', 'private_key', 'cast_url', 'result', 'result_proof', 'use_voter_aliases', 'voting_starts_at', 'voting_ends_at', 'election_type'] + @property + def registration_status_pretty(self): + if self.openreg: + return "Open" + else: + return "Closed" - JSON_FIELDS = ['uuid', 'questions', 'name', 'short_name', 'description', 'voters_hash', 'openreg', - 'frozen_at', 'public_key', 'cast_url', 'use_voter_aliases', 'voting_starts_at', 'voting_ends_at'] + @property + def winners(self): + """ + Depending on the type of each question, determine the winners + returns an array of winners for each question, aka an array of arrays. + assumes that if there is a max to the question, that's how many winners there are. + """ + return [one_question_winner(self.questions[i], self.result[i], self.num_cast_votes) for i in + range(len(self.questions))] - # need to add in v3.1: use_advanced_audit_features, election_type, and probably more + @property + def pretty_result(self): + if not self.result: + return None - def init_tally(self): - return Tally(election=self) + # get the winners + winners = self.winners - def _process_value_in(self, field_name, field_value): - if field_name == 'frozen_at' or field_name == 'voting_starts_at' or field_name == 'voting_ends_at': - if type(field_value) == str or type(field_value) == unicode: - return datetime.datetime.strptime(field_value, '%Y-%m-%d %H:%M:%S') + raw_result = self.result + prettified_result = [] - if field_name == 'public_key': - return algs.EGPublicKey.fromJSONDict(field_value) + # loop through questions + for i in range(len(self.questions)): + q = self.questions[i] + pretty_question = [] - if field_name == 'private_key': - return algs.EGSecretKey.fromJSONDict(field_value) + # go through answers + for j in range(len(q['answers'])): + a = q['answers'][j] + count = raw_result[i][j] + pretty_question.append({'answer': a, 'count': count, 'winner': (j in winners[i])}) - def _process_value_out(self, field_name, field_value): - # the date - if field_name == 'frozen_at' or field_name == 'voting_starts_at' or field_name == 'voting_ends_at': - return str(field_value) + prettified_result.append({'question': q['short_name'], 'answers': pretty_question}) - if field_name == 'public_key' or field_name == 'private_key': - return field_value.toJSONDict() + return prettified_result - @property - def registration_status_pretty(self): - if self.openreg: - return "Open" - else: - return "Closed" - @property - def winners(self): +class Voter(HeliosObject): """ - Depending on the type of each question, determine the winners - returns an array of winners for each question, aka an array of arrays. - assumes that if there is a max to the question, that's how many winners there are. + A voter in an election """ - return [one_question_winner(self.questions[i], self.result[i], self.num_cast_votes) for i in range(len(self.questions))] + FIELDS = ['election_uuid', 'uuid', 'voter_type', 'voter_id', 'name', 'alias'] + JSON_FIELDS = ['election_uuid', 'uuid', 'voter_type', 'voter_id_hash', 'name'] - @property - def pretty_result(self): - if not self.result: - return None + # alternative, for when the voter is aliased + ALIASED_VOTER_JSON_FIELDS = ['election_uuid', 'uuid', 'alias'] - # get the winners - winners = self.winners + def toJSONDict(self): + if self.alias is not None: + return super(Voter, self).toJSONDict(self.ALIASED_VOTER_JSON_FIELDS) + else: + return super(Voter, self).toJSONDict() - raw_result = self.result - prettified_result = [] + @property + def voter_id_hash(self): + if self.voter_login_id: + # for backwards compatibility with v3.0, and since it doesn't matter + # too much if we hash the email or the unique login ID here. + return utils.hash_b64(self.voter_login_id) + else: + return utils.hash_b64(self.voter_id) - # loop through questions - for i in range(len(self.questions)): - q = self.questions[i] - pretty_question = [] - - # go through answers - for j in range(len(q['answers'])): - a = q['answers'][j] - count = raw_result[i][j] - pretty_question.append({'answer': a, 'count': count, 'winner': (j in winners[i])}) - - prettified_result.append({'question': q['short_name'], 'answers': pretty_question}) - - return prettified_result - - -class Voter(HeliosObject): - """ - A voter in an election - """ - FIELDS = ['election_uuid', 'uuid', 'voter_type', 'voter_id', 'name', 'alias'] - JSON_FIELDS = ['election_uuid', 'uuid', 'voter_type', 'voter_id_hash', 'name'] - - # alternative, for when the voter is aliased - ALIASED_VOTER_JSON_FIELDS = ['election_uuid', 'uuid', 'alias'] - - def toJSONDict(self): - fields = None - if self.alias != None: - return super(Voter, self).toJSONDict(self.ALIASED_VOTER_JSON_FIELDS) - else: - return super(Voter,self).toJSONDict() - - @property - def voter_id_hash(self): - if self.voter_login_id: - # for backwards compatibility with v3.0, and since it doesn't matter - # too much if we hash the email or the unique login ID here. - return utils.hash_b64(self.voter_login_id) - else: - return utils.hash_b64(self.voter_id) class Trustee(HeliosObject): - """ - a trustee - """ - FIELDS = ['uuid', 'public_key', 'public_key_hash', 'pok', 'decryption_factors', 'decryption_proofs', 'email'] - - def _process_value_in(self, field_name, field_value): - if field_name == 'public_key': - return algs.EGPublicKey.fromJSONDict(field_value) - - if field_name == 'pok': - return algs.DLogProof.fromJSONDict(field_value) - - def _process_value_out(self, field_name, field_value): - if field_name == 'public_key' or field_name == 'pok': - return field_value.toJSONDict() - -class CastVote(HeliosObject): - """ - A cast vote, which includes an encrypted vote and some cast metadata - """ - FIELDS = ['vote', 'cast_at', 'voter_uuid', 'voter_hash', 'vote_hash'] - - def __init__(self, *args, **kwargs): - super(CastVote, self).__init__(*args, **kwargs) - self.election = None - - @classmethod - def fromJSONDict(cls, d, election=None): - o = cls() - o.election = election - o.set_from_args(**d) - return o - - def toJSONDict(self, include_vote=True): - result = super(CastVote,self).toJSONDict() - if not include_vote: - del result['vote'] - return result - - @classmethod - def fromOtherObject(cls, o, election): - obj = cls() - obj.election = election - obj.set_from_other_object(o) - return obj - - def _process_value_in(self, field_name, field_value): - if field_name == 'cast_at': - if type(field_value) == str: - return datetime.datetime.strptime(field_value, '%Y-%m-%d %H:%M:%S') - - if field_name == 'vote': - return EncryptedVote.fromJSONDict(field_value, self.election.public_key) - - def _process_value_out(self, field_name, field_value): - # the date - if field_name == 'cast_at': - return str(field_value) - - if field_name == 'vote': - return field_value.toJSONDict() - - def issues(self, election): """ - Look for consistency problems + a trustee """ - issues = [] - - # check the election - if self.vote.election_uuid != election.uuid: - issues.append("the vote's election UUID does not match the election for which this vote is being cast") - - return issues - -class DLogTable(object): - """ - Keeping track of discrete logs - """ - - def __init__(self, base, modulus): - self.dlogs = {} - self.dlogs[1] = 0 - self.last_dlog_result = 1 - self.counter = 0 - - self.base = base - self.modulus = modulus + FIELDS = ['uuid', 'public_key', 'public_key_hash', 'pok', 'decryption_factors', 'decryption_proofs', 'email'] - def increment(self): - self.counter += 1 + def _process_value_in(self, field_name, field_value): + if field_name == 'public_key': + return algs.EGPublicKey.fromJSONDict(field_value) - # new value - new_value = (self.last_dlog_result * self.base) % self.modulus + if field_name == 'pok': + return algs.DLogProof.fromJSONDict(field_value) - # record the discrete log - self.dlogs[new_value] = self.counter + def _process_value_out(self, field_name, field_value): + if field_name == 'public_key' or field_name == 'pok': + return field_value.toJSONDict() - # record the last value - self.last_dlog_result = new_value - - def precompute(self, up_to): - while self.counter < up_to: - self.increment() - - def lookup(self, value): - return self.dlogs.get(value, None) +class CastVote(HeliosObject): + """ + A cast vote, which includes an encrypted vote and some cast metadata + """ + FIELDS = ['vote', 'cast_at', 'voter_uuid', 'voter_hash', 'vote_hash'] -class Tally(HeliosObject): - """ - A running homomorphic tally - """ + def __init__(self, *args, **kwargs): + super(CastVote, self).__init__(*args, **kwargs) + self.election = None - FIELDS = ['num_tallied', 'tally'] - JSON_FIELDS = ['num_tallied', 'tally'] + @classmethod + def fromJSONDict(cls, d, election=None): + o = cls() + o.election = election + o.set_from_args(**d) + return o - def __init__(self, *args, **kwargs): - super(Tally, self).__init__(*args, **kwargs) + def toJSONDict(self, include_vote=True): + result = super(CastVote, self).toJSONDict() + if not include_vote: + del result['vote'] + return result - self.election = kwargs.get('election',None) + @classmethod + def fromOtherObject(cls, o, election): + obj = cls() + obj.election = election + obj.set_from_other_object(o) + return obj - if self.election: - self.init_election(self.election) - else: - self.questions = None - self.public_key = None + def _process_value_in(self, field_name, field_value): + if field_name == 'cast_at': + if isinstance(field_value, str): + return datetime.datetime.strptime(field_value, '%Y-%m-%d %H:%M:%S') - if not self.tally: - self.tally = None + if field_name == 'vote': + return EncryptedVote.fromJSONDict(field_value, self.election.public_key) - # initialize - if self.num_tallied == None: - self.num_tallied = 0 + def _process_value_out(self, field_name, field_value): + # the date + if field_name == 'cast_at': + return str(field_value) - def init_election(self, election): - """ - given the election, initialize some params - """ - self.questions = election.questions - self.public_key = election.public_key + if field_name == 'vote': + return field_value.toJSONDict() - if not self.tally: - self.tally = [[0 for a in q['answers']] for q in self.questions] + def issues(self, election): + """ + Look for consistency problems + """ + issues = [] - def add_vote_batch(self, encrypted_votes, verify_p=True): - """ - Add a batch of votes. Eventually, this will be optimized to do an aggregate proof verification - rather than a whole proof verif for each vote. - """ - for vote in encrypted_votes: - self.add_vote(vote, verify_p) - - def add_vote(self, encrypted_vote, verify_p=True): - # do we verify? - if verify_p: - if not encrypted_vote.verify(self.election): - raise Exception('Bad Vote') - - # for each question - for question_num in range(len(self.questions)): - question = self.questions[question_num] - answers = question['answers'] - - # for each possible answer to each question - for answer_num in range(len(answers)): - # do the homomorphic addition into the tally - enc_vote_choice = encrypted_vote.encrypted_answers[question_num].choices[answer_num] - enc_vote_choice.pk = self.public_key - self.tally[question_num][answer_num] = encrypted_vote.encrypted_answers[question_num].choices[answer_num] * self.tally[question_num][answer_num] - - self.num_tallied += 1 - - def decryption_factors_and_proofs(self, sk): - """ - returns an array of decryption factors and a corresponding array of decryption proofs. - makes the decryption factors into strings, for general Helios / JS compatibility. - """ - # for all choices of all questions (double list comprehension) - decryption_factors = [] - decryption_proof = [] + # check the election + if self.vote.election_uuid != election.uuid: + issues.append("the vote's election UUID does not match the election for which this vote is being cast") - for question_num, question in enumerate(self.questions): - answers = question['answers'] - question_factors = [] - question_proof = [] + return issues - for answer_num, answer in enumerate(answers): - # do decryption and proof of it - dec_factor, proof = sk.decryption_factor_and_proof(self.tally[question_num][answer_num]) - # look up appropriate discrete log - # this is the string conversion - question_factors.append(str(dec_factor)) - question_proof.append(proof.toJSONDict()) - - decryption_factors.append(question_factors) - decryption_proof.append(question_proof) - - return decryption_factors, decryption_proof - - def decrypt_and_prove(self, sk, discrete_logs=None): +class DLogTable(object): """ - returns an array of tallies and a corresponding array of decryption proofs. + Keeping track of discrete logs """ - # who's keeping track of discrete logs? - if not discrete_logs: - discrete_logs = self.discrete_logs + def __init__(self, base, modulus): + self.dlogs = {1: 0} + self.last_dlog_result = 1 + self.counter = 0 - # for all choices of all questions (double list comprehension) - decrypted_tally = [] - decryption_proof = [] + self.base = base + self.modulus = modulus - for question_num in range(len(self.questions)): - question = self.questions[question_num] - answers = question['answers'] - question_tally = [] - question_proof = [] + def increment(self): + self.counter += 1 - for answer_num in range(len(answers)): - # do decryption and proof of it - plaintext, proof = sk.prove_decryption(self.tally[question_num][answer_num]) + # new value + new_value = (self.last_dlog_result * self.base) % self.modulus - # look up appropriate discrete log - question_tally.append(discrete_logs[plaintext]) - question_proof.append(proof) + # record the discrete log + self.dlogs[new_value] = self.counter - decrypted_tally.append(question_tally) - decryption_proof.append(question_proof) + # record the last value + self.last_dlog_result = new_value - return decrypted_tally, decryption_proof + def precompute(self, up_to): + while self.counter < up_to: + self.increment() - def verify_decryption_proofs(self, decryption_factors, decryption_proofs, public_key, challenge_generator): - """ - decryption_factors is a list of lists of dec factors - decryption_proofs are the corresponding proofs - public_key is, of course, the public key of the trustee - """ + def lookup(self, value): + return self.dlogs.get(value, None) - # go through each one - for q_num, q in enumerate(self.tally): - for a_num, answer_tally in enumerate(q): - # parse the proof - proof = algs.EGZKProof.fromJSONDict(decryption_proofs[q_num][a_num]) - # check that g, alpha, y, dec_factor is a DH tuple - if not proof.verify(public_key.g, answer_tally.alpha, public_key.y, int(decryption_factors[q_num][a_num]), public_key.p, public_key.q, challenge_generator): - return False - - return True - - def decrypt_from_factors(self, decryption_factors, public_key): +class Tally(HeliosObject): """ - decrypt a tally given decryption factors - - The decryption factors are a list of decryption factor sets, for each trustee. - Each decryption factor set is a list of lists of decryption factors (questions/answers). + A running homomorphic tally """ - # pre-compute a dlog table - dlog_table = DLogTable(base = public_key.g, modulus = public_key.p) - dlog_table.precompute(self.num_tallied) - - result = [] + FIELDS = ['num_tallied', 'tally'] + JSON_FIELDS = ['num_tallied', 'tally'] + + def __init__(self, *args, **kwargs): + super(Tally, self).__init__(*args, **kwargs) + + self.election = kwargs.get('election', None) + + if self.election: + self.init_election(self.election) + else: + self.questions = None + self.public_key = None + + if not self.tally: + self.tally = None + + # initialize + if self.num_tallied is None: + self.num_tallied = 0 + + def init_election(self, election): + """ + given the election, initialize some params + """ + self.questions = election.questions + self.public_key = election.public_key + + if not self.tally: + self.tally = [[0 for _ in q['answers']] for q in self.questions] + + def add_vote_batch(self, encrypted_votes, verify_p=True): + """ + Add a batch of votes. Eventually, this will be optimized to do an aggregate proof verification + rather than a whole proof verif for each vote. + """ + for vote in encrypted_votes: + self.add_vote(vote, verify_p) + + def add_vote(self, encrypted_vote, verify_p=True): + # do we verify? + if verify_p: + if not encrypted_vote.verify(self.election): + raise Exception('Bad Vote') + + # for each question + for question_num in range(len(self.questions)): + question = self.questions[question_num] + answers = question['answers'] + + # for each possible answer to each question + for answer_num in range(len(answers)): + # do the homomorphic addition into the tally + enc_vote_choice = encrypted_vote.encrypted_answers[question_num].choices[answer_num] + enc_vote_choice.pk = self.public_key + self.tally[question_num][answer_num] = encrypted_vote.encrypted_answers[question_num].choices[ + answer_num] * self.tally[question_num][answer_num] + + self.num_tallied += 1 + + def decryption_factors_and_proofs(self, sk): + """ + returns an array of decryption factors and a corresponding array of decryption proofs. + makes the decryption factors into strings, for general Helios / JS compatibility. + """ + # for all choices of all questions (double list comprehension) + decryption_factors = [] + decryption_proof = [] + + for question_num, question in enumerate(self.questions): + answers = question['answers'] + question_factors = [] + question_proof = [] + + for answer_num, answer in enumerate(answers): + # do decryption and proof of it + dec_factor, proof = sk.decryption_factor_and_proof(self.tally[question_num][answer_num]) + + # look up appropriate discrete log + # this is the string conversion + question_factors.append(str(dec_factor)) + question_proof.append(proof.toJSONDict()) + + decryption_factors.append(question_factors) + decryption_proof.append(question_proof) + + return decryption_factors, decryption_proof + + def decrypt_and_prove(self, sk, discrete_logs=None): + """ + returns an array of tallies and a corresponding array of decryption proofs. + """ + + # who's keeping track of discrete logs? + if not discrete_logs: + discrete_logs = self.discrete_logs + + # for all choices of all questions (double list comprehension) + decrypted_tally = [] + decryption_proof = [] + + for question_num in range(len(self.questions)): + question = self.questions[question_num] + answers = question['answers'] + question_tally = [] + question_proof = [] + + for answer_num in range(len(answers)): + # do decryption and proof of it + plaintext, proof = sk.prove_decryption(self.tally[question_num][answer_num]) + + # look up appropriate discrete log + question_tally.append(discrete_logs[plaintext]) + question_proof.append(proof) + + decrypted_tally.append(question_tally) + decryption_proof.append(question_proof) + + return decrypted_tally, decryption_proof + + def verify_decryption_proofs(self, decryption_factors, decryption_proofs, public_key, challenge_generator): + """ + decryption_factors is a list of lists of dec factors + decryption_proofs are the corresponding proofs + public_key is, of course, the public key of the trustee + """ + + # go through each one + for q_num, q in enumerate(self.tally): + for a_num, answer_tally in enumerate(q): + # parse the proof + proof = algs.EGZKProof.fromJSONDict(decryption_proofs[q_num][a_num]) + + # check that g, alpha, y, dec_factor is a DH tuple + if not proof.verify(public_key.g, answer_tally.alpha, public_key.y, + int(decryption_factors[q_num][a_num]), public_key.p, public_key.q, + challenge_generator): + return False + + return True + + def decrypt_from_factors(self, decryption_factors, public_key): + """ + decrypt a tally given decryption factors + + The decryption factors are a list of decryption factor sets, for each trustee. + Each decryption factor set is a list of lists of decryption factors (questions/answers). + """ + + # pre-compute a dlog table + dlog_table = DLogTable(base=public_key.g, modulus=public_key.p) + dlog_table.precompute(self.num_tallied) + + result = [] - # go through each one - for q_num, q in enumerate(self.tally): - q_result = [] + # go through each one + for q_num, q in enumerate(self.tally): + q_result = [] - for a_num, a in enumerate(q): - # coalesce the decryption factors into one list - dec_factor_list = [df[q_num][a_num] for df in decryption_factors] - raw_value = self.tally[q_num][a_num].decrypt(dec_factor_list, public_key) + for a_num, a in enumerate(q): + # coalesce the decryption factors into one list + dec_factor_list = [df[q_num][a_num] for df in decryption_factors] + raw_value = self.tally[q_num][a_num].decrypt(dec_factor_list, public_key) - q_result.append(dlog_table.lookup(raw_value)) + q_result.append(dlog_table.lookup(raw_value)) - result.append(q_result) + result.append(q_result) - return result + return result - def _process_value_in(self, field_name, field_value): - if field_name == 'tally': - return [[algs.EGCiphertext.fromJSONDict(a) for a in q] for q in field_value] + def _process_value_in(self, field_name, field_value): + if field_name == 'tally': + return [[algs.EGCiphertext.fromJSONDict(a) for a in q] for q in field_value] - def _process_value_out(self, field_name, field_value): - if field_name == 'tally': - return [[a.toJSONDict() for a in q] for q in field_value] + def _process_value_out(self, field_name, field_value): + if field_name == 'tally': + return [[a.toJSONDict() for a in q] for q in field_value] diff --git a/helios/crypto/elgamal.py b/helios/crypto/elgamal.py index 88a08c01b..33eb03083 100644 --- a/helios/crypto/elgamal.py +++ b/helios/crypto/elgamal.py @@ -8,12 +8,13 @@ ben@adida.net """ -import math, hashlib, logging -import randpool, number +import logging -import numtheory +from Crypto.Hash import SHA1 +from Crypto.Util.number import inverse + +from helios.crypto.utils import random -from algs import Utils class Cryptosystem(object): def __init__(self): @@ -21,30 +22,6 @@ def __init__(self): self.q = None self.g = None - @classmethod - def generate(cls, n_bits): - """ - generate an El-Gamal environment. Returns an instance - of ElGamal(), with prime p, group size q, and generator g - """ - - EG = cls() - - # find a prime p such that (p-1)/2 is prime q - EG.p = Utils.random_safe_prime(n_bits) - - # q is the order of the group - # FIXME: not always p-1/2 - EG.q = (EG.p-1)/2 - - # find g that generates the q-order subgroup - while True: - EG.g = Utils.random_mpz_lt(EG.p) - if pow(EG.g, EG.q, EG.p) == 1: - break - - return EG - def generate_keypair(self): """ generates a keypair in the setting @@ -68,7 +45,7 @@ def generate(self, p, q, g): self.pk.p = p self.pk.q = q - self.sk.x = Utils.random_mpz_lt(q) + self.sk.x = random.mpz_lt(q) self.pk.y = pow(g, self.sk.x, p) self.sk.public_key = self.pk @@ -106,7 +83,7 @@ def encrypt_return_r(self, plaintext): """ Encrypt a plaintext and return the randomness just generated and used. """ - r = Utils.random_mpz_lt(self.q) + r = random.mpz_lt(self.q) ciphertext = self.encrypt_with_r(plaintext, r) return [ciphertext, r] @@ -181,7 +158,7 @@ def decrypt(self, ciphertext, dec_factor = None, decode_m=False): if not dec_factor: dec_factor = self.decryption_factor(ciphertext) - m = (Utils.inverse(dec_factor, self.pk.p) * ciphertext.beta) % self.pk.p + m = (inverse(dec_factor, self.pk.p) * ciphertext.beta) % self.pk.p if decode_m: # get m back from the q-order subgroup @@ -207,15 +184,15 @@ def prove_decryption(self, ciphertext): and alpha^t = b * beta/m ^ c """ - m = (Utils.inverse(pow(ciphertext.alpha, self.x, self.pk.p), self.pk.p) * ciphertext.beta) % self.pk.p - beta_over_m = (ciphertext.beta * Utils.inverse(m, self.pk.p)) % self.pk.p + m = (inverse(pow(ciphertext.alpha, self.x, self.pk.p), self.pk.p) * ciphertext.beta) % self.pk.p + beta_over_m = (ciphertext.beta * inverse(m, self.pk.p)) % self.pk.p # pick a random w - w = Utils.random_mpz_lt(self.pk.q) + w = random.mpz_lt(self.pk.q) a = pow(self.pk.g, w, self.pk.p) b = pow(ciphertext.alpha, w, self.pk.p) - c = int(hashlib.sha1(str(a) + "," + str(b)).hexdigest(),16) + c = int(SHA1.new(bytes(str(a) + "," + str(b), 'utf-8')).hexdigest(),16) t = (w + self.x * c) % self.pk.q @@ -232,7 +209,7 @@ def prove_sk(self, challenge_generator): Verifier provides challenge modulo q. Prover computes response = w + x*challenge mod q, where x is the secret key. """ - w = Utils.random_mpz_lt(self.pk.q) + w = random.mpz_lt(self.pk.q) commitment = pow(self.pk.g, w, self.pk.p) challenge = challenge_generator(commitment) % self.pk.q response = (w + (self.x * challenge)) % self.pk.q @@ -255,7 +232,7 @@ def __mul__(self,other): """ Homomorphic Multiplication of ciphertexts. """ - if type(other) == int and (other == 0 or other == 1): + if isinstance(other, int) and (other == 0 or other == 1): return self if self.pk != other.pk: @@ -287,7 +264,7 @@ def reenc_return_r(self): """ Reencryption with fresh randomness, which is returned. """ - r = Utils.random_mpz_lt(self.pk.q) + r = random.mpz_lt(self.pk.q) new_c = self.reenc_with_r(r) return [new_c, r] @@ -301,17 +278,17 @@ def __eq__(self, other): """ Check for ciphertext equality. """ - if other == None: + if other is None: return False - return (self.alpha == other.alpha and self.beta == other.beta) + return self.alpha == other.alpha and self.beta == other.beta def generate_encryption_proof(self, plaintext, randomness, challenge_generator): """ Generate the disjunctive encryption proof of encryption """ # random W - w = Utils.random_mpz_lt(self.pk.q) + w = random.mpz_lt(self.pk.q) # build the proof proof = ZKProof() @@ -331,20 +308,20 @@ def generate_encryption_proof(self, plaintext, randomness, challenge_generator): def simulate_encryption_proof(self, plaintext, challenge=None): # generate a random challenge if not provided if not challenge: - challenge = Utils.random_mpz_lt(self.pk.q) + challenge = random.mpz_lt(self.pk.q) proof = ZKProof() proof.challenge = challenge # compute beta/plaintext, the completion of the DH tuple - beta_over_plaintext = (self.beta * Utils.inverse(plaintext.m, self.pk.p)) % self.pk.p + beta_over_plaintext = (self.beta * inverse(plaintext.m, self.pk.p)) % self.pk.p # random response, does not even need to depend on the challenge - proof.response = Utils.random_mpz_lt(self.pk.q); + proof.response = random.mpz_lt(self.pk.q); # now we compute A and B - proof.commitment['A'] = (Utils.inverse(pow(self.alpha, proof.challenge, self.pk.p), self.pk.p) * pow(self.pk.g, proof.response, self.pk.p)) % self.pk.p - proof.commitment['B'] = (Utils.inverse(pow(beta_over_plaintext, proof.challenge, self.pk.p), self.pk.p) * pow(self.pk.y, proof.response, self.pk.p)) % self.pk.p + proof.commitment['A'] = (inverse(pow(self.alpha, proof.challenge, self.pk.p), self.pk.p) * pow(self.pk.g, proof.response, self.pk.p)) % self.pk.p + proof.commitment['B'] = (inverse(pow(beta_over_plaintext, proof.challenge, self.pk.p), self.pk.p) * pow(self.pk.y, proof.response, self.pk.p)) % self.pk.p return proof @@ -397,7 +374,7 @@ def verify_encryption_proof(self, plaintext, proof): first_check = (pow(self.pk.g, proof.response, self.pk.p) == ((pow(self.alpha, proof.challenge, self.pk.p) * proof.commitment['A']) % self.pk.p)) # check that y^response = B * (beta/m)^challenge - beta_over_m = (self.beta * Utils.inverse(plaintext.m, self.pk.p)) % self.pk.p + beta_over_m = (self.beta * inverse(plaintext.m, self.pk.p)) % self.pk.p second_check = (pow(self.pk.y, proof.response, self.pk.p) == ((pow(beta_over_m, proof.challenge, self.pk.p) * proof.commitment['B']) % self.pk.p)) # print "1,2: %s %s " % (first_check, second_check) @@ -416,7 +393,7 @@ def verify_disjunctive_encryption_proof(self, plaintexts, proof, challenge_gener for i in range(len(plaintexts)): # if a proof fails, stop right there if not self.verify_encryption_proof(plaintexts[i], proof.proofs[i]): - print "bad proof %s, %s, %s" % (i, plaintexts[i], proof.proofs[i]) + print("bad proof %s, %s, %s" % (i, plaintexts[i], proof.proofs[i])) return False # logging.info("made it past the two encryption proofs") @@ -444,7 +421,7 @@ def decrypt(self, decryption_factors, public_key): """ running_decryption = self.beta for dec_factor in decryption_factors: - running_decryption = (running_decryption * Utils.inverse(dec_factor, public_key.p)) % public_key.p + running_decryption = (running_decryption * inverse(dec_factor, public_key.p)) % public_key.p return running_decryption @@ -473,7 +450,7 @@ def generate(cls, little_g, little_h, x, p, q, challenge_generator): """ # generate random w - w = Utils.random_mpz_lt(q) + w = random.mpz_lt(q) # create proof instance proof = cls() @@ -526,7 +503,7 @@ def disjunctive_challenge_generator(commitments): array_to_hash.append(str(commitment['B'])) string_to_hash = ",".join(array_to_hash) - return int(hashlib.sha1(string_to_hash).hexdigest(),16) + return int(SHA1.new(bytes(string_to_hash, 'utf-8')).hexdigest(),16) # a challenge generator for Fiat-Shamir with A,B commitment def fiatshamir_challenge_generator(commitment): @@ -534,5 +511,5 @@ def fiatshamir_challenge_generator(commitment): def DLog_challenge_generator(commitment): string_to_hash = str(commitment) - return int(hashlib.sha1(string_to_hash).hexdigest(),16) + return int(SHA1.new(bytes(string_to_hash, 'utf-8')).hexdigest(),16) diff --git a/helios/crypto/number.py b/helios/crypto/number.py deleted file mode 100644 index 9d50563e9..000000000 --- a/helios/crypto/number.py +++ /dev/null @@ -1,201 +0,0 @@ -# -# number.py : Number-theoretic functions -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: number.py,v 1.13 2003/04/04 18:21:07 akuchling Exp $" - -bignum = long -try: - from Crypto.PublicKey import _fastmath -except ImportError: - _fastmath = None - -# Commented out and replaced with faster versions below -## def long2str(n): -## s='' -## while n>0: -## s=chr(n & 255)+s -## n=n>>8 -## return s - -## import types -## def str2long(s): -## if type(s)!=types.StringType: return s # Integers will be left alone -## return reduce(lambda x,y : x*256+ord(y), s, 0L) - -def size (N): - """size(N:long) : int - Returns the size of the number N in bits. - """ - bits, power = 0,1L - while N >= power: - bits += 1 - power = power << 1 - return bits - -def getRandomNumber(N, randfunc): - """getRandomNumber(N:int, randfunc:callable):long - Return an N-bit random number.""" - - S = randfunc(N/8) - odd_bits = N % 8 - if odd_bits != 0: - char = ord(randfunc(1)) >> (8-odd_bits) - S = chr(char) + S - value = bytes_to_long(S) - value |= 2L ** (N-1) # Ensure high bit is set - assert size(value) >= N - return value - -def GCD(x,y): - """GCD(x:long, y:long): long - Return the GCD of x and y. - """ - x = abs(x) ; y = abs(y) - while x > 0: - x, y = y % x, x - return y - -def inverse(u, v): - """inverse(u:long, u:long):long - Return the inverse of u mod v. - """ - u3, v3 = long(u), long(v) - u1, v1 = 1L, 0L - while v3 > 0: - q=u3 / v3 - u1, v1 = v1, u1 - v1*q - u3, v3 = v3, u3 - v3*q - while u1<0: - u1 = u1 + v - return u1 - -# Given a number of bits to generate and a random generation function, -# find a prime number of the appropriate size. - -def getPrime(N, randfunc): - """getPrime(N:int, randfunc:callable):long - Return a random N-bit prime number. - """ - - number=getRandomNumber(N, randfunc) | 1 - while (not isPrime(number)): - number=number+2 - return number - -def isPrime(N): - """isPrime(N:long):bool - Return true if N is prime. - """ - if N == 1: - return 0 - if N in sieve: - return 1 - for i in sieve: - if (N % i)==0: - return 0 - - # Use the accelerator if available - if _fastmath is not None: - return _fastmath.isPrime(N) - - # Compute the highest bit that's set in N - N1 = N - 1L - n = 1L - while (n> 1L - - # Rabin-Miller test - for c in sieve[:7]: - a=long(c) ; d=1L ; t=n - while (t): # Iterate over the bits in N1 - x=(d*d) % N - if x==1L and d!=1L and d!=N1: - return 0 # Square root of 1 found - if N1 & t: - d=(x*a) % N - else: - d=x - t = t >> 1L - if d!=1L: - return 0 - return 1 - -# Small primes used for checking primality; these are all the primes -# less than 256. This should be enough to eliminate most of the odd -# numbers before needing to do a Rabin-Miller test at all. - -sieve=[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, - 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, - 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, - 197, 199, 211, 223, 227, 229, 233, 239, 241, 251] - -# Improved conversion functions contributed by Barry Warsaw, after -# careful benchmarking - -import struct - -def long_to_bytes(n, blocksize=0): - """long_to_bytes(n:long, blocksize:int) : string - Convert a long integer to a byte string. - - If optional blocksize is given and greater than zero, pad the front of the - byte string with binary zeros so that the length is a multiple of - blocksize. - """ - # after much testing, this algorithm was deemed to be the fastest - s = '' - n = long(n) - pack = struct.pack - while n > 0: - s = pack('>I', n & 0xffffffffL) + s - n = n >> 32 - # strip off leading zeros - for i in range(len(s)): - if s[i] != '\000': - break - else: - # only happens when n == 0 - s = '\000' - i = 0 - s = s[i:] - # add back some pad bytes. this could be done more efficiently w.r.t. the - # de-padding being done above, but sigh... - if blocksize > 0 and len(s) % blocksize: - s = (blocksize - len(s) % blocksize) * '\000' + s - return s - -def bytes_to_long(s): - """bytes_to_long(string) : long - Convert a byte string to a long integer. - - This is (essentially) the inverse of long_to_bytes(). - """ - acc = 0L - unpack = struct.unpack - length = len(s) - if length % 4: - extra = (4 - length % 4) - s = '\000' * extra + s - length = length + extra - for i in range(0, length, 4): - acc = (acc << 32) + unpack('>I', s[i:i+4])[0] - return acc - -# For backwards compatibility... -import warnings -def long2str(n, blocksize=0): - warnings.warn("long2str() has been replaced by long_to_bytes()") - return long_to_bytes(n, blocksize) -def str2long(s): - warnings.warn("str2long() has been replaced by bytes_to_long()") - return bytes_to_long(s) diff --git a/helios/crypto/numtheory.py b/helios/crypto/numtheory.py index 16fcf9aa0..e16691745 100644 --- a/helios/crypto/numtheory.py +++ b/helios/crypto/numtheory.py @@ -103,7 +103,7 @@ def trial_division(n, bound=None): if n == 1: return 1 for p in [2, 3, 5]: if n%p == 0: return p - if bound == None: bound = n + if bound is None: bound = n dif = [6, 4, 2, 4, 2, 4, 6, 2] m = 7; i = 1 while m <= bound and m*m <= n: @@ -207,7 +207,7 @@ def inversemod(a, n): """ g, x, y = xgcd(a, n) if g != 1: - raise ZeroDivisionError, (a,n) + raise ZeroDivisionError(a,n) assert g == 1, "a must be coprime to n." return x%n @@ -225,7 +225,7 @@ def solve_linear(a,b,n): Examples: >>> solve_linear(4, 2, 10) 8 - >>> solve_linear(2, 1, 4) == None + >>> solve_linear(2, 1, 4) is None True """ g, c, _ = xgcd(a,n) # (1) @@ -1014,7 +1014,7 @@ def elliptic_curve_method(N, m, tries=5): E, P = randcurve(N) # (2) try: # (3) Q = ellcurve_mul(E, m, P) # (4) - except ZeroDivisionError, x: # (5) + except ZeroDivisionError as x: # (5) g = gcd(x[0],N) # (6) if g != 1 or g != N: return g # (7) return N @@ -1153,7 +1153,7 @@ def __mul__(self, other): return r def __neg__(self): v = {} - for m in self.v.keys(): + for m in list(self.v.keys()): v[m] = -self.v[m] return Poly(v) def __div__(self, other): @@ -1161,7 +1161,7 @@ def __div__(self, other): def __getitem__(self, m): # (6) m = tuple(m) - if not self.v.has_key(m): self.v[m] = 0 + if m not in self.v: self.v[m] = 0 return self.v[m] def __setitem__(self, m, c): self.v[tuple(m)] = c @@ -1169,7 +1169,7 @@ def __delitem__(self, m): del self.v[tuple(m)] def monomials(self): # (7) - return self.v.keys() + return list(self.v.keys()) def normalize(self): # (8) while True: finished = True @@ -1244,8 +1244,8 @@ def prove_associative(): # (15) - (x3 + x4)*(x3 - x4)*(x3 - x4)) s2 = (x3 - x4)*(x3 - x4)*((y1 - y5)*(y1 - y5) \ - (x1 + x5)*(x1 - x5)*(x1 - x5)) - print "Associative?" - print s1 == s2 # (17) + print("Associative?") + print(s1 == s2) # (17) diff --git a/helios/crypto/randpool.py b/helios/crypto/randpool.py deleted file mode 100644 index 53a8acc03..000000000 --- a/helios/crypto/randpool.py +++ /dev/null @@ -1,422 +0,0 @@ -# -# randpool.py : Cryptographically strong random number generation -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: randpool.py,v 1.14 2004/05/06 12:56:54 akuchling Exp $" - -import time, array, types, warnings, os.path -from number import long_to_bytes -try: - import Crypto.Util.winrandom as winrandom -except: - winrandom = None - -STIRNUM = 3 - -class RandomPool: - """randpool.py : Cryptographically strong random number generation. - - The implementation here is similar to the one in PGP. To be - cryptographically strong, it must be difficult to determine the RNG's - output, whether in the future or the past. This is done by using - a cryptographic hash function to "stir" the random data. - - Entropy is gathered in the same fashion as PGP; the highest-resolution - clock around is read and the data is added to the random number pool. - A conservative estimate of the entropy is then kept. - - If a cryptographically secure random source is available (/dev/urandom - on many Unixes, Windows CryptGenRandom on most Windows), then use - it. - - Instance Attributes: - bits : int - Maximum size of pool in bits - bytes : int - Maximum size of pool in bytes - entropy : int - Number of bits of entropy in this pool. - - Methods: - add_event([s]) : add some entropy to the pool - get_bytes(int) : get N bytes of random data - randomize([N]) : get N bytes of randomness from external source - """ - - - def __init__(self, numbytes = 160, cipher=None, hash=None): - if hash is None: - from hashlib import sha1 as hash - - # The cipher argument is vestigial; it was removed from - # version 1.1 so RandomPool would work even in the limited - # exportable subset of the code - if cipher is not None: - warnings.warn("'cipher' parameter is no longer used") - - if isinstance(hash, types.StringType): - # ugly hack to force __import__ to give us the end-path module - hash = __import__('Crypto.Hash.'+hash, - None, None, ['new']) - warnings.warn("'hash' parameter should now be a hashing module") - - self.bytes = numbytes - self.bits = self.bytes*8 - self.entropy = 0 - self._hash = hash - - # Construct an array to hold the random pool, - # initializing it to 0. - self._randpool = array.array('B', [0]*self.bytes) - - self._event1 = self._event2 = 0 - self._addPos = 0 - self._getPos = hash().digest_size - self._lastcounter=time.time() - self.__counter = 0 - - self._measureTickSize() # Estimate timer resolution - self._randomize() - - def _updateEntropyEstimate(self, nbits): - self.entropy += nbits - if self.entropy < 0: - self.entropy = 0 - elif self.entropy > self.bits: - self.entropy = self.bits - - def _randomize(self, N = 0, devname = '/dev/urandom'): - """_randomize(N, DEVNAME:device-filepath) - collects N bits of randomness from some entropy source (e.g., - /dev/urandom on Unixes that have it, Windows CryptoAPI - CryptGenRandom, etc) - DEVNAME is optional, defaults to /dev/urandom. You can change it - to /dev/random if you want to block till you get enough - entropy. - """ - data = '' - if N <= 0: - nbytes = int((self.bits - self.entropy)/8+0.5) - else: - nbytes = int(N/8+0.5) - if winrandom: - # Windows CryptGenRandom provides random data. - data = winrandom.new().get_bytes(nbytes) - # GAE fix, benadida - #elif os.path.exists(devname): - # # Many OSes support a /dev/urandom device - # try: - # f=open(devname) - # data=f.read(nbytes) - # f.close() - # except IOError, (num, msg): - # if num!=2: raise IOError, (num, msg) - # # If the file wasn't found, ignore the error - if data: - self._addBytes(data) - # Entropy estimate: The number of bits of - # data obtained from the random source. - self._updateEntropyEstimate(8*len(data)) - self.stir_n() # Wash the random pool - - def randomize(self, N=0): - """randomize(N:int) - use the class entropy source to get some entropy data. - This is overridden by KeyboardRandomize(). - """ - return self._randomize(N) - - def stir_n(self, N = STIRNUM): - """stir_n(N) - stirs the random pool N times - """ - for i in xrange(N): - self.stir() - - def stir (self, s = ''): - """stir(s:string) - Mix up the randomness pool. This will call add_event() twice, - but out of paranoia the entropy attribute will not be - increased. The optional 's' parameter is a string that will - be hashed with the randomness pool. - """ - - entropy=self.entropy # Save inital entropy value - self.add_event() - - # Loop over the randomness pool: hash its contents - # along with a counter, and add the resulting digest - # back into the pool. - for i in range(self.bytes / self._hash().digest_size): - h = self._hash(self._randpool) - h.update(str(self.__counter) + str(i) + str(self._addPos) + s) - self._addBytes( h.digest() ) - self.__counter = (self.__counter + 1) & 0xFFFFffffL - - self._addPos, self._getPos = 0, self._hash().digest_size - self.add_event() - - # Restore the old value of the entropy. - self.entropy=entropy - - - def get_bytes (self, N): - """get_bytes(N:int) : string - Return N bytes of random data. - """ - - s='' - i, pool = self._getPos, self._randpool - h=self._hash() - dsize = self._hash().digest_size - num = N - while num > 0: - h.update( self._randpool[i:i+dsize] ) - s = s + h.digest() - num = num - dsize - i = (i + dsize) % self.bytes - if i>1, bits+1 - if bits>8: bits=8 - - self._event1, self._event2 = event, self._event1 - - self._updateEntropyEstimate(bits) - return bits - - # Private functions - def _noise(self): - # Adds a bit of noise to the random pool, by adding in the - # current time and CPU usage of this process. - # The difference from the previous call to _noise() is taken - # in an effort to estimate the entropy. - t=time.time() - delta = (t - self._lastcounter)/self._ticksize*1e6 - self._lastcounter = t - self._addBytes(long_to_bytes(long(1000*time.time()))) - self._addBytes(long_to_bytes(long(1000*time.clock()))) - self._addBytes(long_to_bytes(long(1000*time.time()))) - self._addBytes(long_to_bytes(long(delta))) - - # Reduce delta to a maximum of 8 bits so we don't add too much - # entropy as a result of this call. - delta=delta % 0xff - return int(delta) - - - def _measureTickSize(self): - # _measureTickSize() tries to estimate a rough average of the - # resolution of time that you can see from Python. It does - # this by measuring the time 100 times, computing the delay - # between measurements, and taking the median of the resulting - # list. (We also hash all the times and add them to the pool) - interval = [None] * 100 - h = self._hash(`(id(self),id(interval))`) - - # Compute 100 differences - t=time.time() - h.update(`t`) - i = 0 - j = 0 - while i < 100: - t2=time.time() - h.update(`(i,j,t2)`) - j += 1 - delta=int((t2-t)*1e6) - if delta: - interval[i] = delta - i += 1 - t=t2 - - # Take the median of the array of intervals - interval.sort() - self._ticksize=interval[len(interval)/2] - h.update(`(interval,self._ticksize)`) - # mix in the measurement times and wash the random pool - self.stir(h.digest()) - - def _addBytes(self, s): - "XOR the contents of the string S into the random pool" - i, pool = self._addPos, self._randpool - for j in range(0, len(s)): - pool[i]=pool[i] ^ ord(s[j]) - i=(i+1) % self.bytes - self._addPos = i - - # Deprecated method names: remove in PCT 2.1 or later. - def getBytes(self, N): - warnings.warn("getBytes() method replaced by get_bytes()", - DeprecationWarning) - return self.get_bytes(N) - - def addEvent (self, event, s=""): - warnings.warn("addEvent() method replaced by add_event()", - DeprecationWarning) - return self.add_event(s + str(event)) - -class PersistentRandomPool (RandomPool): - def __init__ (self, filename=None, *args, **kwargs): - RandomPool.__init__(self, *args, **kwargs) - self.filename = filename - if filename: - try: - # the time taken to open and read the file might have - # a little disk variability, modulo disk/kernel caching... - f=open(filename, 'rb') - self.add_event() - data = f.read() - self.add_event() - # mix in the data from the file and wash the random pool - self.stir(data) - f.close() - except IOError: - # Oh, well; the file doesn't exist or is unreadable, so - # we'll just ignore it. - pass - - def save(self): - if self.filename == "": - raise ValueError, "No filename set for this object" - # wash the random pool before save, provides some forward secrecy for - # old values of the pool. - self.stir_n() - f=open(self.filename, 'wb') - self.add_event() - f.write(self._randpool.tostring()) - f.close() - self.add_event() - # wash the pool again, provide some protection for future values - self.stir() - -# non-echoing Windows keyboard entry -_kb = 0 -if not _kb: - try: - import msvcrt - class KeyboardEntry: - def getch(self): - c = msvcrt.getch() - if c in ('\000', '\xe0'): - # function key - c += msvcrt.getch() - return c - def close(self, delay = 0): - if delay: - time.sleep(delay) - while msvcrt.kbhit(): - msvcrt.getch() - _kb = 1 - except: - pass - -# non-echoing Posix keyboard entry -if not _kb: - try: - import termios - class KeyboardEntry: - def __init__(self, fd = 0): - self._fd = fd - self._old = termios.tcgetattr(fd) - new = termios.tcgetattr(fd) - new[3]=new[3] & ~termios.ICANON & ~termios.ECHO - termios.tcsetattr(fd, termios.TCSANOW, new) - def getch(self): - termios.tcflush(0, termios.TCIFLUSH) # XXX Leave this in? - return os.read(self._fd, 1) - def close(self, delay = 0): - if delay: - time.sleep(delay) - termios.tcflush(self._fd, termios.TCIFLUSH) - termios.tcsetattr(self._fd, termios.TCSAFLUSH, self._old) - _kb = 1 - except: - pass - -class KeyboardRandomPool (PersistentRandomPool): - def __init__(self, *args, **kwargs): - PersistentRandomPool.__init__(self, *args, **kwargs) - - def randomize(self, N = 0): - "Adds N bits of entropy to random pool. If N is 0, fill up pool." - import os, string, time - if N <= 0: - bits = self.bits - self.entropy - else: - bits = N*8 - if bits == 0: - return - print bits,'bits of entropy are now required. Please type on the keyboard' - print 'until enough randomness has been accumulated.' - kb = KeyboardEntry() - s='' # We'll save the characters typed and add them to the pool. - hash = self._hash - e = 0 - try: - while e < bits: - temp=str(bits-e).rjust(6) - os.write(1, temp) - s=s+kb.getch() - e += self.add_event(s) - os.write(1, 6*chr(8)) - self.add_event(s+hash.new(s).digest() ) - finally: - kb.close() - print '\n\007 Enough. Please wait a moment.\n' - self.stir_n() # wash the random pool. - kb.close(4) - -if __name__ == '__main__': - pool = RandomPool() - print 'random pool entropy', pool.entropy, 'bits' - pool.add_event('something') - print `pool.get_bytes(100)` - import tempfile, os - fname = tempfile.mktemp() - pool = KeyboardRandomPool(filename=fname) - print 'keyboard random pool entropy', pool.entropy, 'bits' - pool.randomize() - print 'keyboard random pool entropy', pool.entropy, 'bits' - pool.randomize(128) - pool.save() - saved = open(fname, 'rb').read() - print 'saved', `saved` - print 'pool ', `pool._randpool.tostring()` - newpool = PersistentRandomPool(fname) - print 'persistent random pool entropy', pool.entropy, 'bits' - os.remove(fname) diff --git a/helios/crypto/utils.py b/helios/crypto/utils.py index dd395a598..2fcce307f 100644 --- a/helios/crypto/utils.py +++ b/helios/crypto/utils.py @@ -1,23 +1,31 @@ """ Crypto Utils """ +import base64 +import math + +from Crypto.Hash import SHA256 +from Crypto.Random.random import StrongRandom + +random = StrongRandom() + + +def random_mpz_lt(maximum, strong_random=random): + n_bits = int(math.floor(math.log(maximum, 2))) + res = strong_random.getrandbits(n_bits) + while res >= maximum: + res = strong_random.getrandbits(n_bits) + return res + + +random.mpz_lt = random_mpz_lt -import hmac, base64, json -from hashlib import sha256 - def hash_b64(s): - """ - hash the string using sha1 and produce a base64 output - removes the trailing "=" - """ - hasher = sha256(s) - result= base64.b64encode(hasher.digest())[:-1] - return result - -def to_json(d): - return json.dumps(d, sort_keys=True) - -def from_json(json_str): - if not json_str: return None - return json.loads(json_str) + """ + hash the string using sha256 and produce a base64 output + removes the trailing "=" + """ + hasher = SHA256.new(s.encode('utf-8')) + result = base64.b64encode(hasher.digest())[:-1].decode('ascii') + return result diff --git a/helios/datatypes/__init__.py b/helios/datatypes/__init__.py index b0ede25f2..574f8eb93 100644 --- a/helios/datatypes/__init__.py +++ b/helios/datatypes/__init__.py @@ -25,6 +25,7 @@ # but is not necessary for full JSON-LD objects. LDObject.deserialize(json_string, type=...) """ +import importlib from helios import utils from helios.crypto import utils as cryptoutils @@ -33,32 +34,32 @@ ## utility function ## def recursiveToDict(obj): - if obj == None: + if obj is None: return None - if type(obj) == list: + if isinstance(obj, list): return [recursiveToDict(el) for el in obj] else: return obj.toDict() def get_class(datatype): # already done? - if not isinstance(datatype, basestring): + if not isinstance(datatype, str): return datatype # parse datatype string "v31/Election" --> from v31 import Election parsed_datatype = datatype.split("/") # get the module - dynamic_module = __import__(".".join(parsed_datatype[:-1]), globals(), locals(), [], level=-1) + dynamic_module = importlib.import_module("helios.datatypes." + (".".join(parsed_datatype[:-1]))) if not dynamic_module: - raise Exception("no module for %s" % datatpye) + raise Exception("no module for %s" % datatype) # go down the attributes to get to the class try: dynamic_ptr = dynamic_module - for attr in parsed_datatype[1:]: + for attr in parsed_datatype[-1:]: dynamic_ptr = getattr(dynamic_ptr, attr) dynamic_cls = dynamic_ptr except AttributeError: @@ -119,7 +120,7 @@ def __init__(self, wrapped_obj): @classmethod def instantiate(cls, obj, datatype=None): - "FIXME: should datatype override the object's internal datatype? probably not" + """FIXME: should datatype override the object's internal datatype? probably not""" if isinstance(obj, LDObject): return obj @@ -130,7 +131,7 @@ def instantiate(cls, obj, datatype=None): raise Exception("no datatype found") # nulls - if obj == None: + if obj is None: return None # the class @@ -149,9 +150,11 @@ def _setattr_wrapped(self, attr, val): setattr(self.wrapped_obj, attr, val) def loadData(self): - "load data using from the wrapped object" + """ + load data using from the wrapped object + """ # go through the subfields and instantiate them too - for subfield_name, subfield_type in self.STRUCTURED_FIELDS.iteritems(): + for subfield_name, subfield_type in self.STRUCTURED_FIELDS.items(): self.structured_fields[subfield_name] = self.instantiate(self._getattr_wrapped(subfield_name), datatype = subfield_type) def loadDataFromDict(self, d): @@ -160,7 +163,7 @@ def loadDataFromDict(self, d): """ # the structured fields - structured_fields = self.STRUCTURED_FIELDS.keys() + structured_fields = list(self.STRUCTURED_FIELDS.keys()) # go through the fields and set them properly # on the newly instantiated object @@ -171,7 +174,7 @@ def loadDataFromDict(self, d): self.structured_fields[f] = sub_ld_object # set the field on the wrapped object too - if sub_ld_object != None: + if sub_ld_object is not None: self._setattr_wrapped(f, sub_ld_object.wrapped_obj) else: self._setattr_wrapped(f, None) @@ -190,12 +193,12 @@ def toDict(self, alternate_fields=None, complete=False): fields = self.FIELDS if not self.structured_fields: - if self.wrapped_obj.alias != None: + if self.wrapped_obj.alias is not None: fields = self.ALIASED_VOTER_FIELDS for f in (alternate_fields or fields): # is it a structured subfield? - if self.structured_fields.has_key(f): + if f in self.structured_fields: val[f] = recursiveToDict(self.structured_fields[f]) else: val[f] = self.process_value_out(f, self._getattr_wrapped(f)) @@ -214,7 +217,7 @@ def toDict(self, alternate_fields=None, complete=False): @classmethod def fromDict(cls, d, type_hint=None): # null objects - if d == None: + if d is None: return None # the LD type is either in d or in type_hint @@ -248,11 +251,11 @@ def process_value_in(self, field_name, field_value): """ process some fields on the way into the object """ - if field_value == None: + if field_value is None: return None val = self._process_value_in(field_name, field_value) - if val != None: + if val is not None: return val else: return field_value @@ -264,23 +267,25 @@ def process_value_out(self, field_name, field_value): """ process some fields on the way out of the object """ - if field_value == None: + if field_value is None: return None val = self._process_value_out(field_name, field_value) - if val != None: + if val is not None: return val else: return field_value def _process_value_out(self, field_name, field_value): + if isinstance(field_value, bytes): + return field_value.decode('utf-8') return None - + def __eq__(self, other): if not hasattr(self, 'uuid'): - return super(LDObject,self) == other + return super(LDObject, self) == other - return other != None and self.uuid == other.uuid + return other is not None and self.uuid == other.uuid class BaseArrayOfObjects(LDObject): diff --git a/helios/datatypes/djangofield.py b/helios/datatypes/djangofield.py index e0eb1b4fb..a299ace6c 100644 --- a/helios/datatypes/djangofield.py +++ b/helios/datatypes/djangofield.py @@ -6,15 +6,12 @@ and adapted to LDObject """ -import datetime -import json from django.db import models -from django.db.models import signals -from django.conf import settings -from django.core.serializers.json import DjangoJSONEncoder +from helios import utils from . import LDObject + class LDObjectField(models.TextField): """ LDObject is a generic textfield that neatly serializes/unserializes @@ -23,9 +20,6 @@ class LDObjectField(models.TextField): deserialization_params added on 2011-01-09 to provide additional hints at deserialization time """ - # Used so to_python() is called - __metaclass__ = models.SubfieldBase - def __init__(self, type_hint=None, **kwargs): self.type_hint = type_hint super(LDObjectField, self).__init__(**kwargs) @@ -34,35 +28,29 @@ def to_python(self, value): """Convert our string value to LDObject after we load it from the DB""" # did we already convert this? - if not isinstance(value, basestring): + if not isinstance(value, str): return value - if value == None: - return None + return self.from_db_value(value) + # noinspection PyUnusedLocal + def from_db_value(self, value, *args, **kwargs): # in some cases, we're loading an existing array or dict, - # we skip this part but instantiate the LD object - if isinstance(value, basestring): - try: - parsed_value = json.loads(value) - except: - raise Exception("value is not JSON parseable, that's bad news") - else: - parsed_value = value - - if parsed_value != None: - "we give the wrapped object back because we're not dealing with serialization types" - return_val = LDObject.fromDict(parsed_value, type_hint = self.type_hint).wrapped_obj - return return_val - else: + # from_json takes care of this duality + parsed_value = utils.from_json(value) + if parsed_value is None: return None + # we give the wrapped object back because we're not dealing with serialization types + return_val = LDObject.fromDict(parsed_value, type_hint=self.type_hint).wrapped_obj + return return_val + def get_prep_value(self, value): """Convert our JSON object to a string before we save""" - if isinstance(value, basestring): + if isinstance(value, str): return value - if value == None: + if value is None: return None # instantiate the proper LDObject to dump it appropriately @@ -71,4 +59,4 @@ def get_prep_value(self, value): def value_to_string(self, obj): value = self._get_val_from_obj(obj) - return self.get_db_prep_value(value) + return self.get_db_prep_value(value, None) diff --git a/helios/datatypes/legacy.py b/helios/datatypes/legacy.py index c0a24ffc2..d469b4418 100644 --- a/helios/datatypes/legacy.py +++ b/helios/datatypes/legacy.py @@ -77,7 +77,7 @@ def toDict(self, complete=False): """ depending on whether the voter is aliased, use different fields """ - if self.wrapped_obj.alias != None: + if self.wrapped_obj.alias is not None: return super(Voter, self).toDict(self.ALIASED_VOTER_FIELDS, complete = complete) else: return super(Voter,self).toDict(complete = complete) diff --git a/helios/datetimewidget.py b/helios/datetimewidget.py index 5a9e0d40a..dfd7ec04a 100644 --- a/helios/datetimewidget.py +++ b/helios/datetimewidget.py @@ -14,7 +14,7 @@ from django.utils.safestring import mark_safe # DATETIMEWIDGET -calbtn = u'''calendar +calbtn = '''calendar ''' class DateTimeWidget(forms.widgets.TextInput): + template_name = '' + class Media: css = { 'all': ( @@ -49,13 +51,13 @@ def render(self, name, value, attrs=None): except: final_attrs['value'] = \ force_unicode(value) - if not final_attrs.has_key('id'): - final_attrs['id'] = u'%s_id' % (name) + if 'id' not in final_attrs: + final_attrs['id'] = '%s_id' % (name) id = final_attrs['id'] jsdformat = self.dformat #.replace('%', '%%') cal = calbtn % (settings.MEDIA_URL, id, id, jsdformat, id) - a = u'%s%s' % (forms.util.flatatt(final_attrs), self.media, cal) + a = '%s%s' % (forms.util.flatatt(final_attrs), self.media, cal) return mark_safe(a) def value_from_datadict(self, data, files, name): @@ -82,12 +84,12 @@ def _has_changed(self, initial, data): Copy of parent's method, but modify value with strftime function before final comparsion """ if data is None: - data_value = u'' + data_value = '' else: data_value = data if initial is None: - initial_value = u'' + initial_value = '' else: initial_value = initial diff --git a/helios/election_url_names.py b/helios/election_url_names.py new file mode 100644 index 000000000..eff9a5ea2 --- /dev/null +++ b/helios/election_url_names.py @@ -0,0 +1,62 @@ +ELECTION_HOME="election@home" +ELECTION_VIEW="election@view" +ELECTION_META="election@meta" +ELECTION_EDIT="election@edit" +ELECTION_SCHEDULE="election@schedule" +ELECTION_EXTEND="election@extend" +ELECTION_ARCHIVE="election@archive" +ELECTION_COPY="election@copy" +ELECTION_BADGE="election@badge" + +ELECTION_TRUSTEES_HOME="election@trustees" +ELECTION_TRUSTEES_VIEW="election@trustees@view" +ELECTION_TRUSTEES_NEW="election@trustees@new" +ELECTION_TRUSTEES_ADD_HELIOS="election@trustees@add-helios" +ELECTION_TRUSTEES_DELETE="election@trustees@delete" + +ELECTION_TRUSTEE_HOME="election@trustee" +ELECTION_TRUSTEE_SEND_URL="election@trustee@send-url" +ELECTION_TRUSTEE_KEY_GENERATOR="election@trustee@key-generator" +ELECTION_TRUSTEE_CHECK_SK="election@trustee@check-sk" +ELECTION_TRUSTEE_UPLOAD_PK="election@trustee@upload-pk" +ELECTION_TRUSTEE_DECRYPT_AND_PROVE="election@trustee@decrypt-and-prove" +ELECTION_TRUSTEE_UPLOAD_DECRYPTION="election@trustee@upload-decryption" + +ELECTION_RESULT="election@result" +ELECTION_RESULT_PROOF="election@result@proof" +ELECTION_BBOARD="election@bboard" +ELECTION_AUDITED_BALLOTS="election@audited-ballots" + +ELECTION_GET_RANDOMNESS="election@get-randomness" +ELECTION_ENCRYPT_BALLOT="election@encrypt-ballot" +ELECTION_QUESTIONS="election@questions" +ELECTION_SET_REG="election@set-reg" +ELECTION_SET_FEATURED="election@set-featured" +ELECTION_SAVE_QUESTIONS="election@save-questions" +ELECTION_REGISTER="election@register" +ELECTION_FREEZE="election@freeze" + +ELECTION_COMPUTE_TALLY="election@compute-tally" +ELECTION_COMBINE_DECRYPTIONS="election@combine-decryptions" +ELECTION_RELEASE_RESULT="election@release-result" + +ELECTION_CAST="election@cast" +ELECTION_CAST_CONFIRM="election@cast-confirm" +ELECTION_PASSWORD_VOTER_LOGIN="election@password-voter-login" +ELECTION_CAST_DONE="election@cast-done" + +ELECTION_POST_AUDITED_BALLOT="election@post-audited-ballot" + +ELECTION_VOTERS_HOME="election@voters" +ELECTION_VOTERS_UPLOAD="election@voters@upload" +ELECTION_VOTERS_UPLOAD_CANCEL="election@voters@upload-cancel" +ELECTION_VOTERS_LIST="election@voters@list" +ELECTION_VOTERS_LIST_PRETTY="election@voters@list-pretty" +ELECTION_VOTERS_ELIGIBILITY="election@voters@eligibility" +ELECTION_VOTERS_EMAIL="election@voters@email" +ELECTION_VOTER="election@voter" +ELECTION_VOTER_DELETE="election@voter@delete" + +ELECTION_BALLOTS_LIST="election@ballots@list" +ELECTION_BALLOTS_VOTER="election@ballots@voter" +ELECTION_BALLOTS_VOTER_LAST="election@ballots@voter@last" diff --git a/helios/election_urls.py b/helios/election_urls.py index 6622c5568..03e447308 100644 --- a/helios/election_urls.py +++ b/helios/election_urls.py @@ -4,91 +4,99 @@ Ben Adida (ben@adida.net) """ -from django.conf.urls import * +from django.urls import path, re_path -from helios.views import * +from helios import views +from helios import election_url_names as names -urlpatterns = patterns('', +urlpatterns = [ # election data that is cryptographically verified - (r'^$', one_election), + path('', views.one_election, name=names.ELECTION_HOME), # metadata that need not be verified - (r'^/meta$', one_election_meta), + path('/meta', views.one_election_meta, name=names.ELECTION_META), # edit election params - (r'^/edit$', one_election_edit), - (r'^/schedule$', one_election_schedule), - (r'^/extend$', one_election_extend), - (r'^/archive$', one_election_archive), - (r'^/copy$', one_election_copy), + path('/edit', views.one_election_edit, name=names.ELECTION_EDIT), + path('/schedule', views.one_election_schedule, name=names.ELECTION_SCHEDULE), + path('/extend', views.one_election_extend, name=names.ELECTION_EXTEND), + path('/archive', views.one_election_archive, name=names.ELECTION_ARCHIVE), + path('/copy', views.one_election_copy, name=names.ELECTION_COPY), # badge - (r'^/badge$', election_badge), + path('/badge', views.election_badge, name=names.ELECTION_BADGE), # adding trustees - (r'^/trustees/$', list_trustees), - (r'^/trustees/view$', list_trustees_view), - (r'^/trustees/new$', new_trustee), - (r'^/trustees/add-helios$', new_trustee_helios), - (r'^/trustees/delete$', delete_trustee), + path('/trustees/', views.list_trustees, name=names.ELECTION_TRUSTEES_HOME), + path('/trustees/view', views.list_trustees_view, name=names.ELECTION_TRUSTEES_VIEW), + path('/trustees/new', views.new_trustee, name=names.ELECTION_TRUSTEES_NEW), + path('/trustees/add-helios', views.new_trustee_helios, name=names.ELECTION_TRUSTEES_ADD_HELIOS), + path('/trustees/delete', views.delete_trustee, name=names.ELECTION_TRUSTEES_DELETE), # trustee pages - (r'^/trustees/(?P[^/]+)/home$', trustee_home), - (r'^/trustees/(?P[^/]+)/sendurl$', trustee_send_url), - (r'^/trustees/(?P[^/]+)/keygenerator$', trustee_keygenerator), - (r'^/trustees/(?P[^/]+)/check-sk$', trustee_check_sk), - (r'^/trustees/(?P[^/]+)/upoad-pk$', trustee_upload_pk), - (r'^/trustees/(?P[^/]+)/decrypt-and-prove$', trustee_decrypt_and_prove), - (r'^/trustees/(?P[^/]+)/upload-decryption$', trustee_upload_decryption), + path('/trustees//home', + views.trustee_home, name=names.ELECTION_TRUSTEE_HOME), + path('/trustees//sendurl', + views.trustee_send_url, name=names.ELECTION_TRUSTEE_SEND_URL), + path('/trustees//keygenerator', + views.trustee_keygenerator, name=names.ELECTION_TRUSTEE_KEY_GENERATOR), + path('/trustees//check-sk', + views.trustee_check_sk, name=names.ELECTION_TRUSTEE_CHECK_SK), + path('/trustees//upoad-pk', + views.trustee_upload_pk, name=names.ELECTION_TRUSTEE_UPLOAD_PK), + path('/trustees//decrypt-and-prove', + views.trustee_decrypt_and_prove, name=names.ELECTION_TRUSTEE_DECRYPT_AND_PROVE), + path('/trustees//upload-decryption', + views.trustee_upload_decryption, name=names.ELECTION_TRUSTEE_UPLOAD_DECRYPTION), # election voting-process actions - (r'^/view$', one_election_view), - (r'^/result$', one_election_result), - (r'^/result_proof$', one_election_result_proof), - # (r'^/bboard$', one_election_bboard), - (r'^/audited-ballots/$', one_election_audited_ballots), + path('/view', views.one_election_view, name=names.ELECTION_VIEW), + path('/result', views.one_election_result, name=names.ELECTION_RESULT), + path('/result_proof', views.one_election_result_proof, name=names.ELECTION_RESULT_PROOF), + # url(r'^/bboard$', views.one_election_bboard, name=names.ELECTION_BBOARD), + path('/audited-ballots/', views.one_election_audited_ballots, name=names.ELECTION_AUDITED_BALLOTS), # get randomness - (r'^/get-randomness$', get_randomness), + path('/get-randomness', views.get_randomness, name=names.ELECTION_GET_RANDOMNESS), # server-side encryption - (r'^/encrypt-ballot$', encrypt_ballot), + path('/encrypt-ballot', views.encrypt_ballot, name=names.ELECTION_ENCRYPT_BALLOT), # construct election - (r'^/questions$', one_election_questions), - (r'^/set_reg$', one_election_set_reg), - (r'^/set_featured$', one_election_set_featured), - (r'^/save_questions$', one_election_save_questions), - (r'^/register$', one_election_register), - (r'^/freeze$', one_election_freeze), # includes freeze_2 as POST target + path('/questions', views.one_election_questions, name=names.ELECTION_QUESTIONS), + path('/set_reg', views.one_election_set_reg, name=names.ELECTION_SET_REG), + path('/set_featured', views.one_election_set_featured, name=names.ELECTION_SET_FEATURED), + path('/save_questions', views.one_election_save_questions, name=names.ELECTION_SAVE_QUESTIONS), + path('/register', views.one_election_register, name=names.ELECTION_REGISTER), + path('/freeze', views.one_election_freeze, name=names.ELECTION_FREEZE), # includes freeze_2 as POST target # computing tally - (r'^/compute_tally$', one_election_compute_tally), - (r'^/combine_decryptions$', combine_decryptions), - (r'^/release_result$', release_result), + path('/compute_tally', views.one_election_compute_tally, name=names.ELECTION_COMPUTE_TALLY), + path('/combine_decryptions', views.combine_decryptions, name=names.ELECTION_COMBINE_DECRYPTIONS), + path('/release_result', views.release_result, name=names.ELECTION_RELEASE_RESULT), # casting a ballot before we know who the voter is - (r'^/cast$', one_election_cast), - (r'^/cast_confirm$', one_election_cast_confirm), - (r'^/password_voter_login$', password_voter_login), - (r'^/cast_done$', one_election_cast_done), + path('/cast', views.one_election_cast, name=names.ELECTION_CAST), + path('/cast_confirm', views.one_election_cast_confirm, name=names.ELECTION_CAST_CONFIRM), + path('/password_voter_login', views.password_voter_login, name=names.ELECTION_PASSWORD_VOTER_LOGIN), + path('/cast_done', views.one_election_cast_done, name=names.ELECTION_CAST_DONE), # post audited ballot - (r'^/post-audited-ballot', post_audited_ballot), + re_path(r'^/post-audited-ballot', views.post_audited_ballot, name=names.ELECTION_POST_AUDITED_BALLOT), # managing voters - (r'^/voters/$', voter_list), - (r'^/voters/upload$', voters_upload), - (r'^/voters/upload-cancel$', voters_upload_cancel), - (r'^/voters/list$', voters_list_pretty), - (r'^/voters/eligibility$', voters_eligibility), - (r'^/voters/email$', voters_email), - (r'^/voters/(?P[^/]+)$', one_voter), - (r'^/voters/(?P[^/]+)/delete$', voter_delete), + path('/voters/', views.voter_list, name=names.ELECTION_VOTERS_LIST), + path('/voters/upload', views.voters_upload, name=names.ELECTION_VOTERS_UPLOAD), + path('/voters/upload-cancel', views.voters_upload_cancel, name=names.ELECTION_VOTERS_UPLOAD_CANCEL), + path('/voters/list', views.voters_list_pretty, name=names.ELECTION_VOTERS_LIST_PRETTY), + path('/voters/eligibility', views.voters_eligibility, name=names.ELECTION_VOTERS_ELIGIBILITY), + path('/voters/email', views.voters_email, name=names.ELECTION_VOTERS_EMAIL), + path('/voters/', views.one_voter, name=names.ELECTION_VOTER), + path('/voters//delete', views.voter_delete, name=names.ELECTION_VOTER_DELETE), # ballots - (r'^/ballots/$', ballot_list), - (r'^/ballots/(?P[^/]+)/all$', voter_votes), - (r'^/ballots/(?P[^/]+)/last$', voter_last_vote), + path('/ballots/', views.ballot_list, name=names.ELECTION_BALLOTS_LIST), + path('/ballots//all', views.voter_votes, name=names.ELECTION_BALLOTS_VOTER), + path('/ballots//last', views.voter_last_vote, name=names.ELECTION_BALLOTS_VOTER_LAST), -) +] diff --git a/helios/fields.py b/helios/fields.py index cf2ad6c3e..8d8e885fe 100644 --- a/helios/fields.py +++ b/helios/fields.py @@ -1,9 +1,9 @@ -from time import strptime, strftime import datetime -from django import forms -from django.db import models + from django.forms import fields -from widgets import SplitSelectDateTimeWidget + +from .widgets import SplitSelectDateTimeWidget + class SplitDateTimeField(fields.MultiValueField): widget = SplitSelectDateTimeWidget diff --git a/helios/fixtures/election.json b/helios/fixtures/election.json deleted file mode 100644 index cd7a61655..000000000 --- a/helios/fixtures/election.json +++ /dev/null @@ -1,25 +0,0 @@ -[{"pk": 1000, - "model": "helios.election", - "fields": - { - "admin": 1, - "uuid" : "206ef039-05c9-4e9c-bb8f-963da50c08d4", - "short_name" : "test", - "name" : "Test Election", - "election_type" : "election", - "use_advanced_audit_features" : true, - "created_at" : "2013-02-22 12:00:00", - "modified_at" : "2013-02-22 12:00:00", - "private_p" : false, - "description" : "test description", - "public_key" : null, - "private_key" : null, - "questions" : [], - "eligibility": null, - "openreg": true, - "featured_p": false, - "use_voter_aliases" : false, - "cast_url" : "/helios/elections/206ef039-05c9-4e9c-bb8f-963da50c08d4/cast" - } - } - ] diff --git a/helios/fixtures/users.json b/helios/fixtures/users.json index c588ce475..c6e18b53d 100644 --- a/helios/fixtures/users.json +++ b/helios/fixtures/users.json @@ -1 +1,40 @@ -[{"pk": 1, "model": "helios_auth.user", "fields": {"info": "{}", "user_id": "ben@adida.net", "name": "Ben Adida", "user_type": "google", "token": null, "admin_p": false}},{"pk": 2, "model": "helios_auth.user", "fields": {"info": "{}", "user_id": "12345", "name": "Ben Adida", "user_type": "facebook", "token": {"access_token":"1234"}, "admin_p": false}}] \ No newline at end of file +[ + { + "pk": 1, + "model": "helios_auth.user", + "fields": { + "info": "{}", + "user_id": "ben@adida.net", + "name": "Ben Adida", + "user_type": "google", + "token": null, + "admin_p": false + } + }, + { + "pk": 2, + "model": "helios_auth.user", + "fields": { + "info": "{}", + "user_id": "12345", + "name": "Ben Adida", + "user_type": "facebook", + "token": { + "access_token": "1234" + }, + "admin_p": false + } + }, + { + "pk": 3, + "model": "helios_auth.user", + "fields": { + "info": "{}", + "user_id": "mccio@github.com", + "name": "Marco Ciotola", + "user_type": "google", + "token": null, + "admin_p": true + } + } +] \ No newline at end of file diff --git a/helios/fixtures/voter-badfile.csv b/helios/fixtures/voter-badfile.csv index fd674a999..cc858ace2 100644 --- a/helios/fixtures/voter-badfile.csv +++ b/helios/fixtures/voter-badfile.csv @@ -1,5 +1,5 @@ -Ben78@adida.net,Ben78 Adida - benadida5,ben5@adida.net , Ben5 Adida -benadida6,ben6@adida.net,Ben6 Adida -benadida7,ben7@adida.net,Ben7 Adida -ernesto,helios-testing-ernesto@adida.net,Erñesto Testing Helios \ No newline at end of file +password,Ben78@adida.net,Ben78 Adida +password, benadida5,ben5@adida.net , Ben5 Adida +password,benadida6,ben6@adida.net,Ben6 Adida +password,benadida7,ben7@adida.net,Ben7 Adida +password,ernesto,helios-testing-ernesto@adida.net,Erñesto Testing Helios \ No newline at end of file diff --git a/helios/fixtures/voter-file-latin1.csv b/helios/fixtures/voter-file-latin1.csv new file mode 100644 index 000000000..4c2e42a48 --- /dev/null +++ b/helios/fixtures/voter-file-latin1.csv @@ -0,0 +1,4 @@ +password, benadida5,ben5@adida.net , Ben5 Adida +password,benadida6,ben6@adida.net,Ben6 Adida +password,benadida7,ben7@adida.net,Ben7 Adida +password,testlatin1,Test Latin1,J�NIO LUIZ CORREIA J�NIOR diff --git a/helios/fixtures/voter-file.csv b/helios/fixtures/voter-file.csv index b94bd1a4f..70795def3 100644 --- a/helios/fixtures/voter-file.csv +++ b/helios/fixtures/voter-file.csv @@ -1,4 +1,4 @@ - benadida5,ben5@adida.net , Ben5 Adida -benadida6,ben6@adida.net,Ben6 Adida -benadida7,ben7@adida.net,Ben7 Adida -ernesto,helios-testing-ernesto@adida.net,Erñesto Testing Helios \ No newline at end of file +password, benadida5,ben5@adida.net , Ben5 Adida +password,benadida6,ben6@adida.net,Ben6 Adida +password,benadida7,ben7@adida.net,Ben7 Adida +password,ernesto,helios-testing-ernesto@adida.net,Erñesto Testing Helios \ No newline at end of file diff --git a/helios/forms.py b/helios/forms.py index cb10cfab8..d79196758 100644 --- a/helios/forms.py +++ b/helios/forms.py @@ -3,11 +3,12 @@ """ from django import forms -from models import Election -from widgets import * -from fields import * from django.conf import settings +from .fields import SplitDateTimeField +from .models import Election +from .widgets import SplitSelectDateTimeWidget + class ElectionForm(forms.Form): short_name = forms.SlugField(max_length=40, help_text='no spaces, will be part of the URL for your election, e.g. my-club-2010') diff --git a/helios/management/commands/helios_trustee_decrypt.py b/helios/management/commands/helios_trustee_decrypt.py index 3dc75a4af..478833020 100644 --- a/helios/management/commands/helios_trustee_decrypt.py +++ b/helios/management/commands/helios_trustee_decrypt.py @@ -8,12 +8,10 @@ 2010-05-22 """ -from django.core.management.base import BaseCommand, CommandError -import csv, datetime +from django.core.management.base import BaseCommand -from helios import utils as helios_utils +from helios.models import Trustee -from helios.models import * class Command(BaseCommand): args = '' diff --git a/helios/management/commands/load_voter_files.py b/helios/management/commands/load_voter_files.py index 5b82285a6..1e3a79bee 100644 --- a/helios/management/commands/load_voter_files.py +++ b/helios/management/commands/load_voter_files.py @@ -8,12 +8,15 @@ 2010-05-22 """ -from django.core.management.base import BaseCommand, CommandError -import csv, datetime +import datetime + +import csv +import uuid +from django.core.management.base import BaseCommand from helios import utils as helios_utils +from helios.models import User, Voter, VoterFile -from helios.models import * ## ## UTF8 craziness for CSV @@ -25,44 +28,47 @@ def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs): dialect=dialect, **kwargs) for row in csv_reader: # decode UTF-8 back to Unicode, cell by cell: - yield [unicode(cell, 'utf-8') for cell in row] + yield [str(cell, 'utf-8') for cell in row] + def utf_8_encoder(unicode_csv_data): for line in unicode_csv_data: yield line.encode('utf-8') - + + def process_csv_file(election, f): reader = unicode_csv_reader(f) - + num_voters = 0 for voter in reader: - # bad line - if len(voter) < 1: - continue - - num_voters += 1 - voter_id = voter[0] - name = voter_id - email = voter_id - - if len(voter) > 1: - email = voter[1] - - if len(voter) > 2: - name = voter[2] - - # create the user - user = User.update_or_create(user_type='password', user_id=voter_id, info = {'password': helios_utils.random_string(10), 'email': email, 'name': name}) - user.save() - - # does voter for this user already exist - voter = Voter.get_by_election_and_user(election, user) - - # create the voter - if not voter: - voter_uuid = str(uuid.uuid1()) - voter = Voter(uuid= voter_uuid, voter_type = 'password', voter_id = voter_id, name = name, election = election) - voter.save() + # bad line + if len(voter) < 1: + continue + + num_voters += 1 + voter_id = voter[0] + name = voter_id + email = voter_id + + if len(voter) > 1: + email = voter[1] + + if len(voter) > 2: + name = voter[2] + + # create the user + user = User.update_or_create(user_type='password', user_id=voter_id, + info={'password': helios_utils.random_string(10), 'email': email, 'name': name}) + user.save() + + # does voter for this user already exist + voter = Voter.get_by_election_and_user(election, user) + + # create the voter + if not voter: + voter_uuid = str(uuid.uuid1()) + voter = Voter(uuid=voter_uuid, voter_type='password', voter_id=voter_id, name=name, election=election) + voter.save() return num_voters @@ -70,7 +76,7 @@ def process_csv_file(election, f): class Command(BaseCommand): args = '' help = 'load up voters from unprocessed voter files' - + def handle(self, *args, **options): # load up the voter files in order of last uploaded files_to_process = VoterFile.objects.filter(processing_started_at=None).order_by('uploaded_at') @@ -86,5 +92,3 @@ def handle(self, *args, **options): file_to_process.processing_finished_at = datetime.datetime.utcnow() file_to_process.num_voters = num_voters file_to_process.save() - - diff --git a/helios/management/commands/verify_cast_votes.py b/helios/management/commands/verify_cast_votes.py index 5b7f39253..e2fab7186 100644 --- a/helios/management/commands/verify_cast_votes.py +++ b/helios/management/commands/verify_cast_votes.py @@ -6,12 +6,10 @@ 2010-05-22 """ -from django.core.management.base import BaseCommand, CommandError -import csv, datetime +from django.core.management.base import BaseCommand -from helios import utils as helios_utils +from helios.models import CastVote -from helios.models import * def get_cast_vote_to_verify(): # fixme: add "select for update" functionality here diff --git a/helios/media/static_templates/question.html b/helios/media/static_templates/question.html index 5e576916d..15b388253 100644 --- a/helios/media/static_templates/question.html +++ b/helios/media/static_templates/question.html @@ -4,7 +4,7 @@

{#if $T.admin_p}[ {#if $T.question$index > 0}^] [{#/if} -x] [edit] {#/if}{$T.question$index + 1}. {$T.question.question} ({$T.question.choice_type}, select between {$T.question.min} and {#if $T.question.max != null}{$T.question.max}{#else}unlimited{#/if} answers, result type {$T.question.result_type}.)

+x] [edit] {#/if}{$T.question$index + 1}. {$T.question.question} ({$T.question.choice_type}, select between {$T.question.min} and {#if $T.question.max != null}{$T.question.max}{#else}unlimited{#/if} answers, result type {$T.question.result_type}{#if $T.question.randomize_answer_order}, random answer order{#/if}.)
    {#foreach $T.question.answers as answer}
  • {$T.answer} @@ -55,6 +55,13 @@

    +   +   +Random Answer Order:  +

    @@ -115,6 +122,15 @@

    Add a Question:

    +   +   +Random Answer Order:  + +

    +

    diff --git a/helios/migrations/0001_initial.py b/helios/migrations/0001_initial.py index cb2a41e67..886b37021 100644 --- a/helios/migrations/0001_initial.py +++ b/helios/migrations/0001_initial.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals from django.db import models, migrations + +import helios.datatypes import helios.datatypes.djangofield import helios_auth.jsonfield -import helios.datatypes class Migration(migrations.Migration): @@ -87,7 +87,7 @@ class Migration(migrations.Migration): ('result_proof', helios_auth.jsonfield.JSONField(null=True)), ('help_email', models.EmailField(max_length=75, null=True)), ('election_info_url', models.CharField(max_length=300, null=True)), - ('admin', models.ForeignKey(to='helios_auth.User')), + ('admin', models.ForeignKey(to='helios_auth.User', on_delete=models.CASCADE)), ], options={ 'abstract': False, @@ -100,7 +100,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('log', models.CharField(max_length=500)), ('at', models.DateTimeField(auto_now_add=True)), - ('election', models.ForeignKey(to='helios.Election')), + ('election', models.ForeignKey(to='helios.Election', on_delete=models.CASCADE)), ], options={ }, @@ -120,7 +120,7 @@ class Migration(migrations.Migration): ('pok', helios.datatypes.djangofield.LDObjectField(null=True)), ('decryption_factors', helios.datatypes.djangofield.LDObjectField(null=True)), ('decryption_proofs', helios.datatypes.djangofield.LDObjectField(null=True)), - ('election', models.ForeignKey(to='helios.Election')), + ('election', models.ForeignKey(to='helios.Election', on_delete=models.CASCADE)), ], options={ }, @@ -139,8 +139,8 @@ class Migration(migrations.Migration): ('vote', helios.datatypes.djangofield.LDObjectField(null=True)), ('vote_hash', models.CharField(max_length=100, null=True)), ('cast_at', models.DateTimeField(null=True)), - ('election', models.ForeignKey(to='helios.Election')), - ('user', models.ForeignKey(to='helios_auth.User', null=True)), + ('election', models.ForeignKey(to='helios.Election', on_delete=models.CASCADE)), + ('user', models.ForeignKey(to='helios_auth.User', null=True, on_delete=models.CASCADE)), ], options={ }, @@ -156,7 +156,7 @@ class Migration(migrations.Migration): ('processing_started_at', models.DateTimeField(null=True)), ('processing_finished_at', models.DateTimeField(null=True)), ('num_voters', models.IntegerField(null=True)), - ('election', models.ForeignKey(to='helios.Election')), + ('election', models.ForeignKey(to='helios.Election', on_delete=models.CASCADE)), ], options={ }, @@ -173,13 +173,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='castvote', name='voter', - field=models.ForeignKey(to='helios.Voter'), + field=models.ForeignKey(to='helios.Voter', on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='auditedballot', name='election', - field=models.ForeignKey(to='helios.Election'), + field=models.ForeignKey(to='helios.Election', on_delete=models.CASCADE), preserve_default=True, ), ] diff --git a/helios/migrations/0002_castvote_cast_ip.py b/helios/migrations/0002_castvote_cast_ip.py index 47db5b169..bb7a42263 100644 --- a/helios/migrations/0002_castvote_cast_ip.py +++ b/helios/migrations/0002_castvote_cast_ip.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals from django.db import models, migrations diff --git a/helios/migrations/0003_auto_20160507_1948.py b/helios/migrations/0003_auto_20160507_1948.py index 162d6bb54..8e6f65266 100644 --- a/helios/migrations/0003_auto_20160507_1948.py +++ b/helios/migrations/0003_auto_20160507_1948.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals from django.db import models, migrations diff --git a/helios/migrations/0004_auto_20170528_2025.py b/helios/migrations/0004_auto_20170528_2025.py index d49bb6375..a437c5f11 100644 --- a/helios/migrations/0004_auto_20170528_2025.py +++ b/helios/migrations/0004_auto_20170528_2025.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals from django.db import migrations, models diff --git a/helios/migrations/0005_auto_20210123_0941.py b/helios/migrations/0005_auto_20210123_0941.py new file mode 100644 index 000000000..355687c60 --- /dev/null +++ b/helios/migrations/0005_auto_20210123_0941.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2021-01-23 09:41 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('helios', '0004_auto_20170528_2025'), + ] + + operations = [ + migrations.AlterField( + model_name='election', + name='datatype', + field=models.CharField(default='legacy/Election', max_length=250), + ), + migrations.AlterField( + model_name='election', + name='election_type', + field=models.CharField(choices=[('election', 'Election'), ('referendum', 'Referendum')], default='election', max_length=250), + ), + migrations.AlterField( + model_name='voterfile', + name='voter_file', + field=models.FileField(max_length=250, null=True, upload_to='voters/%Y/%m/%d'), + ), + ] diff --git a/helios/models.py b/helios/models.py index 96b665418..95444b217 100644 --- a/helios/models.py +++ b/helios/models.py @@ -6,36 +6,34 @@ (ben@adida.net) """ -from django.db import models, transaction -import json -from django.conf import settings -from django.core.mail import send_mail +import copy +import csv +import datetime +import uuid -import datetime, logging, uuid, random, io import bleach - -from crypto import electionalgs, algs, utils -from helios import utils as heliosutils -import helios.views +from django.conf import settings +from django.db import models, transaction +from validate_email import validate_email from helios import datatypes - - +from helios import utils +from helios.datatypes.djangofield import LDObjectField # useful stuff in helios_auth -from helios_auth.models import User, AUTH_SYSTEMS from helios_auth.jsonfield import JSONField -from helios.datatypes.djangofield import LDObjectField +from helios_auth.models import User, AUTH_SYSTEMS +from .crypto import algs +from .crypto.elgamal import Cryptosystem +from .crypto.utils import random, hash_b64 -import csv, copy -import unicodecsv class HeliosModel(models.Model, datatypes.LDObjectContainer): class Meta: abstract = True class Election(HeliosModel): - admin = models.ForeignKey(User) - + admin = models.ForeignKey(User, on_delete=models.CASCADE) + uuid = models.CharField(max_length=50, null=False) # keep track of the type and version of election, which will help dispatch to the right @@ -44,10 +42,10 @@ class Election(HeliosModel): # v3.1 will still use legacy/Election # later versions, at some point will upgrade to "2011/01/Election" datatype = models.CharField(max_length=250, null=False, default="legacy/Election") - + short_name = models.CharField(max_length=100, unique=True) name = models.CharField(max_length=250) - + ELECTION_TYPES = ( ('election', 'Election'), ('referendum', 'Referendum') @@ -61,10 +59,10 @@ class Election(HeliosModel): null=True) private_key = LDObjectField(type_hint = 'legacy/EGSecretKey', null=True) - + questions = LDObjectField(type_hint = 'legacy/Questions', null=True) - + # eligibility is a JSON field, which lists auth_systems and eligibility details for that auth_system, e.g. # [{'auth_system': 'cas', 'constraint': [{'year': 'u12'}, {'year':'u13'}]}, {'auth_system' : 'password'}, {'auth_system' : 'openid', 'constraint': [{'host':'http://myopenid.com'}]}] eligibility = LDObjectField(type_hint = 'legacy/Eligibility', @@ -74,10 +72,10 @@ class Election(HeliosModel): # this is now used to indicate the state of registration, # whether or not the election is frozen openreg = models.BooleanField(default=False) - + # featured election? featured_p = models.BooleanField(default=False) - + # voter aliases? use_voter_aliases = models.BooleanField(default=False) @@ -86,18 +84,18 @@ class Election(HeliosModel): # randomize candidate order? randomize_answer_order = models.BooleanField(default=False, null=False) - + # where votes should be cast cast_url = models.CharField(max_length = 500) # dates at which this was touched created_at = models.DateTimeField(auto_now_add=True) modified_at = models.DateTimeField(auto_now_add=True) - + # dates at which things happen for the election frozen_at = models.DateTimeField(auto_now_add=False, default=None, null=True) archived_at = models.DateTimeField(auto_now_add=False, default=None, null=True) - + # dates for the election steps, as scheduled # these are always UTC registration_starts_at = models.DateTimeField(auto_now_add=False, default=None, null=True) @@ -111,7 +109,7 @@ class Election(HeliosModel): complaint_period_ends_at = models.DateTimeField(auto_now_add=False, default=None, null=True) tallying_starts_at = models.DateTimeField(auto_now_add=False, default=None, null=True) - + # dates when things were forced to be performed voting_started_at = models.DateTimeField(auto_now_add=False, default=None, null=True) voting_extended_until = models.DateTimeField(auto_now_add=False, default=None, null=True) @@ -125,7 +123,7 @@ class Election(HeliosModel): # the hash of all voters (stored for large numbers) voters_hash = models.CharField(max_length=100, null=True) - + # encrypted tally, each a JSON string # used only for homomorphic tallies encrypted_tally = LDObjectField(type_hint = 'legacy/Tally', @@ -145,6 +143,9 @@ class Election(HeliosModel): # downloadable election info election_info_url = models.CharField(max_length=300, null=True) + class Meta: + app_label = 'helios' + # metadata for the election @property def metadata(self): @@ -179,28 +180,32 @@ def last_alias_num(self): """ if not self.use_voter_aliases: return None - - return heliosutils.one_val_raw_sql("select max(cast(substring(alias, 2) as integer)) from " + Voter._meta.db_table + " where election_id = %s", [self.id]) or 0 + + return utils.one_val_raw_sql("select max(cast(substr(alias, 2) as integer)) from " + Voter._meta.db_table + " where election_id = %s", [self.id]) or 0 @property def encrypted_tally_hash(self): if not self.encrypted_tally: return None - return utils.hash_b64(self.encrypted_tally.toJSON()) + return hash_b64(self.encrypted_tally.toJSON()) @property def is_archived(self): - return self.archived_at != None + return self.archived_at is not None @property def description_bleached(self): - return bleach.clean(self.description, tags = bleach.ALLOWED_TAGS + ['p', 'h4', 'h5', 'h3', 'h2', 'br', 'u']) + return bleach.clean(self.description, + tags=list(bleach.ALLOWED_TAGS) + ['p', 'h4', 'h5', 'h3', 'h2', 'br', 'u'], + strip=True, + strip_comments=True, + ) @classmethod def get_featured(cls): return cls.objects.filter(featured_p = True).order_by('short_name') - + @classmethod def get_or_create(cls, **kwargs): return cls.objects.get_or_create(short_name = kwargs['short_name'], defaults=kwargs) @@ -208,43 +213,43 @@ def get_or_create(cls, **kwargs): @classmethod def get_by_user_as_admin(cls, user, archived_p=None, limit=None): query = cls.objects.filter(admin = user) - if archived_p == True: + if archived_p is True: query = query.exclude(archived_at= None) - if archived_p == False: + if archived_p is False: query = query.filter(archived_at= None) query = query.order_by('-created_at') if limit: return query[:limit] else: return query - + @classmethod def get_by_user_as_voter(cls, user, archived_p=None, limit=None): query = cls.objects.filter(voter__user = user) - if archived_p == True: + if archived_p is True: query = query.exclude(archived_at= None) - if archived_p == False: + if archived_p is False: query = query.filter(archived_at= None) query = query.order_by('-created_at') if limit: return query[:limit] else: return query - + @classmethod def get_by_uuid(cls, uuid): try: return cls.objects.select_related().get(uuid=uuid) except cls.DoesNotExist: return None - + @classmethod def get_by_short_name(cls, short_name): try: return cls.objects.get(short_name=short_name) except cls.DoesNotExist: return None - + def save_questions_safely(self, questions): """ Because Django doesn't let us override properties in a Pythonic way... doing the brute-force thing. @@ -254,11 +259,11 @@ def save_questions_safely(self, questions): for answer_url in q['answer_urls']: if not answer_url or answer_url == "": continue - + # abort saving if bad URL if not (answer_url[:7] == "http://" or answer_url[:8]== "https://"): return False - + self.questions = questions return True @@ -266,16 +271,20 @@ def add_voters_file(self, uploaded_file): """ expects a django uploaded_file data structure, which has filename, content, size... """ - # now we're just storing the content - # random_filename = str(uuid.uuid4()) - # new_voter_file.voter_file.save(random_filename, uploaded_file) + voter_file_content_bytes = uploaded_file.read() + + # usually it's utf-8 encoded, but occasionally it's latin-1 + try: + voter_file_content = voter_file_content_bytes.decode('utf-8') + except: + voter_file_content = voter_file_content_bytes.decode('latin-1') - new_voter_file = VoterFile(election = self, voter_file_content = uploaded_file.read()) + new_voter_file = VoterFile(election = self, voter_file_content = voter_file_content) new_voter_file.save() - + self.append_log(ElectionLog.VOTER_FILE_ADDED) return new_voter_file - + def user_eligible_p(self, user): """ Checks if a user is eligible for this election. @@ -283,15 +292,15 @@ def user_eligible_p(self, user): # registration closed, then eligibility doesn't come into play if not self.openreg: return False - - if self.eligibility == None: + + if self.eligibility is None: return True - + # is the user eligible for one of these cases? for eligibility_case in self.eligibility: if user.is_eligible_for(eligibility_case): return True - + return False def eligibility_constraint_for(self, user_type): @@ -299,7 +308,7 @@ def eligibility_constraint_for(self, user_type): return [] # constraints that are relevant - relevant_constraints = [constraint['constraint'] for constraint in self.eligibility if constraint['auth_system'] == user_type and constraint.has_key('constraint')] + relevant_constraints = [constraint['constraint'] for constraint in self.eligibility if constraint['auth_system'] == user_type and 'constraint' in constraint] if len(relevant_constraints) > 0: return relevant_constraints[0] else: @@ -309,23 +318,23 @@ def eligibility_category_id(self, user_type): "when eligibility is by category, this returns the category_id" if not self.eligibility: return None - + constraint_for = self.eligibility_constraint_for(user_type) if len(constraint_for) > 0: constraint = constraint_for[0] return AUTH_SYSTEMS[user_type].eligibility_category_id(constraint) else: return None - + @property def pretty_eligibility(self): if not self.eligibility: return "Anyone can vote." else: return_val = "
      " - + for constraint in self.eligibility: - if constraint.has_key('constraint'): + if 'constraint' in constraint: for one_constraint in constraint['constraint']: return_val += "
    • %s
    • " % AUTH_SYSTEMS[constraint['auth_system']].pretty_eligibility(one_constraint) else: @@ -334,30 +343,46 @@ def pretty_eligibility(self): return_val += "
    " return return_val - + + @property + def voting_start_at(self): + voting_start_at = self.voting_starts_at + if voting_start_at and self.frozen_at: + voting_start_at = max(voting_start_at, self.frozen_at) + return voting_start_at + + @property + def voting_end_at(self): + voting_end_at = self.voting_ends_at + if voting_end_at and self.voting_extended_until: + voting_end_at = max(voting_end_at, self.voting_extended_until) + if voting_end_at and self.voting_ended_at: + voting_end_at = min(voting_end_at, self.voting_ended_at) + return voting_end_at + def voting_has_started(self): """ has voting begun? voting begins if the election is frozen, at the prescribed date or at the date that voting was forced to start """ - return self.frozen_at != None and (self.voting_starts_at == None or (datetime.datetime.utcnow() >= (self.voting_started_at or self.voting_starts_at))) - + return self.frozen_at is not None and (self.voting_starts_at is None or (datetime.datetime.utcnow() >= (self.voting_started_at or self.voting_starts_at))) + def voting_has_stopped(self): """ has voting stopped? if tally computed, yes, otherwise if we have passed the date voting was manually stopped at, or failing that the date voting was extended until, or failing that the date voting is scheduled to end at. """ voting_end = self.voting_ended_at or self.voting_extended_until or self.voting_ends_at - return (voting_end != None and datetime.datetime.utcnow() >= voting_end) or self.encrypted_tally + return (voting_end is not None and datetime.datetime.utcnow() >= voting_end) or self.encrypted_tally @property def issues_before_freeze(self): issues = [] - if self.questions == None or len(self.questions) == 0: + if self.questions is None or len(self.questions) == 0: issues.append( {'type': 'questions', 'action': "add questions to the ballot"} ) - + trustees = Trustee.get_by_election(self) if len(trustees) == 0: issues.append({ @@ -366,7 +391,7 @@ def issues_before_freeze(self): }) for t in trustees: - if t.public_key == None: + if t.public_key is None: issues.append({ 'type': 'trustee keypairs', 'action': 'have trustee %s generate a keypair' % t.name @@ -378,7 +403,7 @@ def issues_before_freeze(self): "action" : 'enter your voter list (or open registration to the public)' }) - return issues + return issues def ready_for_tallying(self): return datetime.datetime.utcnow() >= self.tallying_starts_at @@ -392,11 +417,11 @@ def compute_tally(self): tally.add_vote(voter.vote, verify_p=False) self.encrypted_tally = tally - self.save() - + self.save() + def ready_for_decryption(self): - return self.encrypted_tally != None - + return self.encrypted_tally is not None + def ready_for_decryption_combination(self): """ do we have a tally from all trustees? @@ -404,9 +429,9 @@ def ready_for_decryption_combination(self): for t in Trustee.get_by_election(self): if not t.decryption_factors: return False - + return True - + def release_result(self): """ release the result that should already be computed @@ -415,22 +440,22 @@ def release_result(self): return self.result_released_at = datetime.datetime.utcnow() - + def combine_decryptions(self): """ combine all of the decryption results """ - + # gather the decryption factors trustees = Trustee.get_by_election(self) decryption_factors = [t.decryption_factors for t in trustees] - + self.result = self.encrypted_tally.decrypt_from_factors(decryption_factors, self.public_key) self.append_log(ElectionLog.DECRYPTIONS_COMBINED) self.save() - + def generate_voters_hash(self): """ look up the list of voters, make a big file, and hash it @@ -444,16 +469,16 @@ def generate_voters_hash(self): else: voters = Voter.get_by_election(self) voters_json = utils.to_json([v.toJSONDict() for v in voters]) - self.voters_hash = utils.hash_b64(voters_json) - + self.voters_hash = hash_b64(voters_json) + def increment_voters(self): ## FIXME return 0 - + def increment_cast_votes(self): ## FIXME return 0 - + def set_eligibility(self): """ if registration is closed and eligibility has not been @@ -468,15 +493,16 @@ def set_eligibility(self): """ # don't override existing eligibility - if self.eligibility != None: + if self.eligibility is not None: return # enable this ONLY once the cast_confirm screen makes sense #if self.voter_set.count() == 0: # return - auth_systems = copy.copy(settings.AUTH_ENABLED_AUTH_SYSTEMS) - voter_types = [r['user__user_type'] for r in self.voter_set.values('user__user_type').distinct() if r['user__user_type'] != None] + auth_systems = copy.copy(settings.AUTH_ENABLED_SYSTEMS) + voter_types = [r['user__user_type'] for r in self.voter_set.values('user__user_type').distinct() if + r['user__user_type'] is not None] # password is now separate, not an explicit voter type if self.voter_set.filter(user=None).count() > 0: @@ -484,7 +510,7 @@ def set_eligibility(self): else: # no password users, remove password from the possible auth systems if 'password' in auth_systems: - auth_systems.remove('password') + auth_systems.remove('password') # closed registration: limit the auth_systems to just the ones # that have registered voters @@ -492,8 +518,8 @@ def set_eligibility(self): auth_systems = [vt for vt in voter_types if vt in auth_systems] self.eligibility = [{'auth_system': auth_system} for auth_system in auth_systems] - self.save() - + self.save() + def freeze(self): """ election is frozen when the voter registration, questions, and trustees are finalized @@ -502,20 +528,20 @@ def freeze(self): raise Exception("cannot freeze an election that has issues") self.frozen_at = datetime.datetime.utcnow() - + # voters hash self.generate_voters_hash() self.set_eligibility() - + # public key for trustees - trustees = Trustee.get_by_election(self) + trustees = list(Trustee.get_by_election(self)) combined_pk = trustees[0].public_key for t in trustees[1:]: combined_pk = combined_pk * t.public_key - + self.public_key = combined_pk - + # log it self.append_log(ElectionLog.FROZEN) @@ -525,6 +551,7 @@ def generate_trustee(self, params): """ generate a trustee including the secret key, thus a helios-based trustee + :type params: Cryptosystem """ # FIXME: generate the keypair keypair = params.generate_keypair() @@ -536,7 +563,7 @@ def generate_trustee(self, params): trustee.email = settings.DEFAULT_FROM_EMAIL trustee.public_key = keypair.pk trustee.secret_key = keypair.sk - + # FIXME: is this at the right level of abstraction? trustee.public_key_hash = datatypes.LDObject.instantiate(trustee.public_key, datatype='legacy/EGPublicKey').hash @@ -550,9 +577,9 @@ def get_helios_trustee(self): return trustees_with_sk[0] else: return None - + def has_helios_trustee(self): - return self.get_helios_trustee() != None + return self.get_helios_trustee() is not None def helios_trustee_decrypt(self): tally = self.encrypted_tally @@ -575,13 +602,14 @@ def get_log(self): @property def url(self): + import helios.views return helios.views.get_election_url(self) def init_tally(self): # FIXME: create the right kind of tally from helios.workflows import homomorphic return homomorphic.Tally(election=self) - + @property def registration_status_pretty(self): if self.openreg: @@ -595,9 +623,9 @@ def one_question_winner(cls, question, result, num_cast_votes): determining the winner for one question """ # sort the answers , keep track of the index - counts = sorted(enumerate(result), key=lambda(x): x[1]) + counts = sorted(enumerate(result), key=lambda x: x[1]) counts.reverse() - + the_max = question['max'] or 1 the_min = question['min'] or 0 @@ -613,7 +641,7 @@ def one_question_winner(cls, question, result, num_cast_votes): return [] else: # assumes that anything non-absolute is relative - return [counts[0][0]] + return [counts[0][0]] @property def winners(self): @@ -623,12 +651,12 @@ def winners(self): assumes that if there is a max to the question, that's how many winners there are. """ return [self.one_question_winner(self.questions[i], self.result[i], self.num_cast_votes) for i in range(len(self.questions))] - + @property def pretty_result(self): if not self.result: return None - + # get the winners winners = self.winners @@ -639,17 +667,18 @@ def pretty_result(self): for i in range(len(self.questions)): q = self.questions[i] pretty_question = [] - + # go through answers for j in range(len(q['answers'])): a = q['answers'][j] count = raw_result[i][j] pretty_question.append({'answer': a, 'count': count, 'winner': (j in winners[i])}) - + prettified_result.append({'question': q['short_name'], 'answers': pretty_question}) return prettified_result - + + class ElectionLog(models.Model): """ a log of events for an election @@ -659,39 +688,22 @@ class ElectionLog(models.Model): VOTER_FILE_ADDED = "voter file added" DECRYPTIONS_COMBINED = "decryptions combined" - election = models.ForeignKey(Election) + election = models.ForeignKey(Election, on_delete=models.CASCADE) log = models.CharField(max_length=500) at = models.DateTimeField(auto_now_add=True) -## -## UTF8 craziness for CSV -## + class Meta: + app_label = 'helios' + -def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs): - # csv.py doesn't do Unicode; encode temporarily as UTF-8: - csv_reader = csv.reader(utf_8_encoder(unicode_csv_data), - dialect=dialect, **kwargs) - for row in csv_reader: - # decode UTF-8 back to Unicode, cell by cell: - try: - yield [unicode(cell, 'utf-8') for cell in row] - except: - yield [unicode(cell, 'latin-1') for cell in row] - -def utf_8_encoder(unicode_csv_data): - for line in unicode_csv_data: - # FIXME: this used to be line.encode('utf-8'), - # need to figure out why this isn't consistent - yield line - class VoterFile(models.Model): """ A model to store files that are lists of voters to be processed """ - # path where we store voter upload + # path where we store voter upload PATH = settings.VOTER_UPLOAD_REL_PATH - election = models.ForeignKey(Election) + election = models.ForeignKey(Election, on_delete=models.CASCADE) # we move to storing the content in the DB voter_file = models.FileField(upload_to=PATH, max_length=250,null=True) @@ -702,88 +714,101 @@ class VoterFile(models.Model): processing_finished_at = models.DateTimeField(auto_now_add=False, null=True) num_voters = models.IntegerField(null=True) + class Meta: + app_label = 'helios' + def itervoters(self): if self.voter_file_content: - if type(self.voter_file_content) == unicode: - content = self.voter_file_content.encode('utf-8') - else: + if isinstance(self.voter_file_content, str): content = self.voter_file_content + elif isinstance(self.voter_file_content, bytes): + content = self.voter_file_content.decode('utf-8') + else: + raise TypeError("voter_file_content is of type {0} instead of str or bytes" + .format(str(type(self.voter_file_content)))) # now we have to handle non-universal-newline stuff # we do this in a simple way: replace all \r with \n # then, replace all double \n with single \n # this should leave us with only \n - content = content.replace('\r','\n').replace('\n\n','\n') - - voter_stream = io.BytesIO(content) + # We then split the contents by line + content = content.replace('\r', '\n').replace('\n\n', '\n').split('\n') else: - voter_stream = open(self.voter_file.path, "rU") + content = open(self.voter_file.path, encoding='utf-8', newline='') - #reader = unicode_csv_reader(voter_stream) - reader = unicodecsv.reader(voter_stream, encoding='utf-8') + reader = csv.reader(content, delimiter=',') for voter_fields in reader: # bad line - if len(voter_fields) < 1: + if len(voter_fields) < 2: continue - - return_dict = {'voter_id': voter_fields[0].strip()} - if len(voter_fields) > 1: - return_dict['email'] = voter_fields[1].strip() - else: - # assume single field means the email is the same field - return_dict['email'] = voter_fields[0].strip() + voter_type = voter_fields[0].strip() + voter_id = voter_fields[1].strip() + + if not voter_type in AUTH_SYSTEMS: + raise Exception("invalid voter type '%s' for voter id '%s', available voter types are %s" % (voter_type, voter_id, ",".join(AUTH_SYSTEMS.keys()))) + # default to having email be the same as voter_id + voter_email = voter_id if len(voter_fields) > 2: - return_dict['name'] = voter_fields[2].strip() - else: - return_dict['name'] = return_dict['email'] + # but if it's supplied, it will be the 3rd field. + voter_email = voter_fields[2].strip() + if voter_type == "password" and not validate_email(voter_email): + raise Exception("invalid voter email '%s' for voter id '%s'" % (voter_email, voter_id)) + + # same thing for voter display name. + voter_name = voter_email + if len(voter_fields) > 3: + # which is supplied as the 4th field if known. + voter_name = voter_fields[3].strip() + + yield { + 'voter_type': voter_type, + 'voter_id': voter_id, + 'email': voter_email, + 'name': voter_name, + } - yield return_dict - def process(self): self.processing_started_at = datetime.datetime.utcnow() self.save() - election = self.election - last_alias_num = election.last_alias_num - - num_voters = 0 - new_voters = [] - for voter in self.itervoters(): - num_voters += 1 - - # does voter for this user already exist - existing_voter = Voter.get_by_election_and_voter_id(election, voter['voter_id']) - - # create the voter - if not existing_voter: - voter_uuid = str(uuid.uuid4()) - existing_voter = Voter(uuid= voter_uuid, user = None, voter_login_id = voter['voter_id'], - voter_name = voter['name'], voter_email = voter['email'], election = election) - existing_voter.generate_password() - new_voters.append(existing_voter) - existing_voter.save() - - if election.use_voter_aliases: - voter_alias_integers = range(last_alias_num+1, last_alias_num+1+num_voters) - random.shuffle(voter_alias_integers) - for i, voter in enumerate(new_voters): - voter.alias = 'V%s' % voter_alias_integers[i] - voter.save() + voters = list(self.itervoters()) + self.num_voters = len(voters) + random.shuffle(voters) + + for voter in voters: + if voter['voter_type'] == 'password': + # does voter for this user already exist + existing_voter = Voter.get_by_election_and_voter_id(self.election, voter['voter_id']) + if existing_voter: + continue + # create the voter + voter_uuid = str(uuid.uuid4()) + new_voter = Voter(uuid=voter_uuid, user = None, voter_login_id = voter['voter_id'], + voter_name = voter['name'], voter_email = voter['email'], election = self.election) + new_voter.generate_password() + election=self.election + if election.use_voter_aliases: + utils.lock_row(Election, election.id) + alias_num = election.last_alias_num + 1 + new_voter.alias = "V%s" % alias_num + new_voter.save() + else: + user, _ = User.objects.get_or_create(user_type=voter['voter_type'], user_id=voter['voter_id'], defaults = {'name': voter['voter_id'], 'info': {}, 'token': None}) + existing_voter = Voter.get_by_election_and_user(self.election, user) + if not existing_voter: + Voter.register_user_in_election(user, self.election) - self.num_voters = num_voters self.processing_finished_at = datetime.datetime.utcnow() self.save() - return num_voters - + return self.num_voters - class Voter(HeliosModel): - election = models.ForeignKey(Election) - + election = models.ForeignKey(Election, on_delete=models.CASCADE) + # let's link directly to the user now # FIXME: delete this as soon as migrations are set up #name = models.CharField(max_length = 200, null=True) @@ -794,32 +819,32 @@ class Voter(HeliosModel): # for users of type password, no user object is created # but a dynamic user object is created automatically - user = models.ForeignKey('helios_auth.User', null=True) + user = models.ForeignKey('helios_auth.User', null=True, on_delete=models.CASCADE) # if user is null, then you need a voter login ID and password voter_login_id = models.CharField(max_length = 100, null=True) voter_password = models.CharField(max_length = 100, null=True) voter_name = models.CharField(max_length = 200, null=True) voter_email = models.CharField(max_length = 250, null=True) - + # if election uses aliases alias = models.CharField(max_length = 100, null=True) - + # we keep a copy here for easy tallying - vote = LDObjectField(type_hint = 'legacy/EncryptedVote', - null=True) + vote = LDObjectField(type_hint = 'legacy/EncryptedVote', null=True) vote_hash = models.CharField(max_length = 100, null=True) cast_at = models.DateTimeField(auto_now_add=False, null=True) class Meta: unique_together = (('election', 'voter_login_id')) + app_label = 'helios' def __init__(self, *args, **kwargs): super(Voter, self).__init__(*args, **kwargs) def get_user(self): # stub the user so code is not full of IF statements - return self.user or User(user_type='password', user_id=self.voter_email, name=self.voter_name) + return self.user or User(user_type='password', user_id=self.voter_email, name=self.voter_name, info={}) @classmethod @transaction.atomic @@ -829,7 +854,7 @@ def register_user_in_election(cls, user, election): # do we need to generate an alias? if election.use_voter_aliases: - heliosutils.lock_row(Election, election.id) + utils.lock_row(Election, election.id) alias_num = election.last_alias_num + 1 voter.alias = "V%s" % alias_num @@ -842,19 +867,19 @@ def get_by_election(cls, election, cast=None, order_by='voter_login_id', after=N FIXME: review this for non-GAE? """ query = cls.objects.filter(election = election) - + # the boolean check is not stupid, this is ternary logic # none means don't care if it's cast or not - if cast == True: + if cast is True: query = query.exclude(cast_at = None) - elif cast == False: + elif cast is False: query = query.filter(cast_at = None) # little trick to get around GAE limitation # order by uuid only when no inequality has been added - if cast == None or order_by == 'cast_at' or order_by =='-cast_at': + if cast is None or order_by == 'cast_at' or order_by == '-cast_at': query = query.order_by(order_by) - + # if we want the list after a certain UUID, add the inequality here if after: if order_by[0] == '-': @@ -863,12 +888,12 @@ def get_by_election(cls, election, cast=None, order_by='voter_login_id', after=N field_name = "%s__gt" % order_by conditions = {field_name : after} query = query.filter (**conditions) - + if limit: query = query[:limit] - + return query - + @classmethod def get_all_by_election_in_chunks(cls, election, cast=None, chunk=100): return cls.get_by_election(election) @@ -879,14 +904,14 @@ def get_by_election_and_voter_id(cls, election, voter_id): return cls.objects.get(election = election, voter_login_id = voter_id) except cls.DoesNotExist: return None - + @classmethod def get_by_election_and_user(cls, election, user): try: return cls.objects.get(election = election, user = user) except cls.DoesNotExist: return None - + @classmethod def get_by_election_and_uuid(cls, election, uuid): query = cls.objects.filter(election = election, uuid = uuid) @@ -911,7 +936,7 @@ def vote_tinyhash(self): """ if not self.vote_hash: return None - + return CastVote.objects.get(vote_hash = self.vote_hash).vote_tinyhash @property @@ -936,12 +961,12 @@ def voter_id_hash(self): value_to_hash = self.voter_id try: - return utils.hash_b64(value_to_hash) + return hash_b64(value_to_hash) except: try: - return utils.hash_b64(value_to_hash.encode('latin-1')) + return hash_b64(value_to_hash.encode('latin-1')) except: - return utils.hash_b64(value_to_hash.encode('utf-8')) + return hash_b64(value_to_hash.encode('utf-8')) @property def voter_type(self): @@ -950,18 +975,18 @@ def voter_type(self): @property def display_html_big(self): return self.get_user().display_html_big - + def send_message(self, subject, body): self.get_user().send_message(subject, body) - + def can_update_status(self): return self.get_user().can_update_status() def generate_password(self, length=10): if self.voter_password: raise Exception("password already exists") - - self.voter_password = heliosutils.random_string(length, alphabet='abcdefghjkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789') + + self.voter_password = utils.random_string(length, alphabet='abcdefghjkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789') def store_vote(self, cast_vote): # only store the vote if it's cast later than the current one @@ -972,15 +997,15 @@ def store_vote(self, cast_vote): self.vote_hash = cast_vote.vote_hash self.cast_at = cast_vote.cast_at self.save() - + def last_cast_vote(self): return CastVote(vote = self.vote, vote_hash = self.vote_hash, cast_at = self.cast_at, voter=self) - - + + class CastVote(HeliosModel): # the reference to the voter provides the voter_uuid - voter = models.ForeignKey(Voter) - + voter = models.ForeignKey(Voter, on_delete=models.CASCADE) + # the actual encrypted vote vote = LDObjectField(type_hint = 'legacy/EncryptedVote') @@ -999,18 +1024,21 @@ class CastVote(HeliosModel): # when is the vote verified? verified_at = models.DateTimeField(null=True) invalidated_at = models.DateTimeField(null=True) - + # auditing purposes, like too many votes from the same IP, if it isn't expected cast_ip = models.GenericIPAddressField(null=True) + class Meta: + app_label = 'helios' + @property def datatype(self): return self.voter.datatype.replace('Voter', 'CastVote') @property def voter_uuid(self): - return self.voter.uuid - + return self.voter.uuid + @property def voter_hash(self): return self.voter.hash @@ -1023,17 +1051,17 @@ def set_tinyhash(self): """ find a tiny version of the hash for a URL slug. """ - safe_hash = self.vote_hash - for c in ['/', '+']: - safe_hash = safe_hash.replace(c,'') - + safe_hash = self.vote_hash.decode() if isinstance(self.vote_hash, bytes) else self.vote_hash + for c in ['/', '+', '#']: + safe_hash = safe_hash.replace(c, '') + length = 8 while True: vote_tinyhash = safe_hash[:length] if CastVote.objects.filter(vote_tinyhash = vote_tinyhash).count() == 0: break length += 1 - + self.vote_tinyhash = vote_tinyhash def save(self, *args, **kwargs): @@ -1045,7 +1073,7 @@ def save(self, *args, **kwargs): self.set_tinyhash() super(CastVote, self).save(*args, **kwargs) - + @classmethod def get_by_voter(cls, voter): return cls.objects.filter(voter = voter).order_by('-cast_at') @@ -1061,13 +1089,13 @@ def verify_and_store(self): self.verified_at = datetime.datetime.utcnow() else: self.invalidated_at = datetime.datetime.utcnow() - + # save and store the vote as the voter's last cast vote self.save() if result: self.voter.store_vote(self) - + return result def issues(self, election): @@ -1075,22 +1103,25 @@ def issues(self, election): Look for consistency problems """ issues = [] - + # check the election if self.vote.election_uuid != election.uuid: issues.append("the vote's election UUID does not match the election for which this vote is being cast") - + return issues - + class AuditedBallot(models.Model): """ ballots for auditing """ - election = models.ForeignKey(Election) + election = models.ForeignKey(Election, on_delete=models.CASCADE) raw_vote = models.TextField() vote_hash = models.CharField(max_length=100) added_at = models.DateTimeField(auto_now_add=True) + class Meta: + app_label = 'helios' + @classmethod def get(cls, election, vote_hash): return cls.objects.get(election = election, vote_hash = vote_hash) @@ -1107,15 +1138,16 @@ def get_by_election(cls, election, after=None, limit=None): query = query[:limit] return query - + + class Trustee(HeliosModel): - election = models.ForeignKey(Election) - + election = models.ForeignKey(Election, on_delete=models.CASCADE) + uuid = models.CharField(max_length=50) name = models.CharField(max_length=200) email = models.EmailField() secret = models.CharField(max_length=100) - + # public key public_key = LDObjectField(type_hint = 'legacy/EGPublicKey', null=True) @@ -1126,11 +1158,11 @@ class Trustee(HeliosModel): # Helios is playing the role of the trustee. secret_key = LDObjectField(type_hint = 'legacy/EGSecretKey', null=True) - + # proof of knowledge of secret key pok = LDObjectField(type_hint = 'legacy/DLogProof', null=True) - + # decryption factors decryption_factors = LDObjectField(type_hint = datatypes.arrayOf(datatypes.arrayOf('core/BigInteger')), null=True) @@ -1140,18 +1172,19 @@ class Trustee(HeliosModel): class Meta: unique_together = (('election', 'email')) - + app_label = 'helios' + def save(self, *args, **kwargs): """ override this just to get a hook """ # not saved yet? if not self.secret: - self.secret = heliosutils.random_string(12) + self.secret = utils.random_string(12) self.election.append_log("Trustee %s added" % self.name) - + super(Trustee, self).save(*args, **kwargs) - + @classmethod def get_by_election(cls, election): return cls.objects.filter(election = election) @@ -1159,7 +1192,7 @@ def get_by_election(cls, election): @classmethod def get_by_uuid(cls, uuid): return cls.objects.get(uuid = uuid) - + @classmethod def get_by_election_and_uuid(cls, election, uuid): return cls.objects.get(election = election, uuid = uuid) @@ -1173,12 +1206,11 @@ def get_by_election_and_email(cls, election, email): @property def datatype(self): - return self.election.datatype.replace('Election', 'Trustee') - + return self.election.datatype.replace('Election', 'Trustee') + def verify_decryption_proofs(self): """ verify that the decryption proofs match the tally for the election """ # verify_decryption_proofs(self, decryption_factors, decryption_proofs, public_key, challenge_generator): return self.election.encrypted_tally.verify_decryption_proofs(self.decryption_factors, self.decryption_proofs, self.public_key, algs.EG_fiatshamir_challenge_generator) - diff --git a/helios/security.py b/helios/security.py index 971499e7c..2f0852d95 100644 --- a/helios/security.py +++ b/helios/security.py @@ -4,21 +4,38 @@ Ben Adida (ben@adida.net) """ +import urllib.parse # nicely update the wrapper function from functools import update_wrapper -from django.core.urlresolvers import reverse -from django.core.exceptions import * -from django.http import * from django.conf import settings +from django.core.exceptions import PermissionDenied +from django.http import Http404 +from django.http import HttpResponseRedirect +from django.urls import reverse -from models import * +import helios from helios_auth.security import get_user +from .models import Voter, Trustee, Election -from django.http import HttpResponseRedirect -import urllib -import helios +class HSTSMiddleware: + def __init__(self, get_response): + self.get_response = get_response + # One-time configuration and initialization. + + def __call__(self, request): + # Code to be executed for each request before + # the view (and later middleware) are called. + + response = self.get_response(request) + + # Code to be executed for each request/response after + # the view is called. + + if settings.STS: + response['Strict-Transport-Security'] = "max-age=31536000; includeSubDomains; preload" + return response # current voter def get_voter(request, user, election): @@ -26,7 +43,7 @@ def get_voter(request, user, election): return the current voter """ voter = None - if request.session.has_key('CURRENT_VOTER_ID'): + if 'CURRENT_VOTER_ID' in request.session: voter = Voter.objects.get(id=request.session['CURRENT_VOTER_ID']) if voter.election != election: voter = None @@ -40,7 +57,7 @@ def get_voter(request, user, election): # a function to check if the current user is a trustee HELIOS_TRUSTEE_UUID = 'helios_trustee_uuid' def get_logged_in_trustee(request): - if request.session.has_key(HELIOS_TRUSTEE_UUID): + if HELIOS_TRUSTEE_UUID in request.session: return Trustee.get_by_uuid(request.session[HELIOS_TRUSTEE_UUID]) else: return None @@ -53,26 +70,26 @@ def set_logged_in_trustee(request, trustee): # def do_election_checks(election, props): # frozen - if props.has_key('frozen'): + if 'frozen' in props: frozen = props['frozen'] else: frozen = None # newvoters (open for registration) - if props.has_key('newvoters'): + if 'newvoters' in props: newvoters = props['newvoters'] else: newvoters = None # frozen check - if frozen != None: + if frozen is not None: if frozen and not election.frozen_at: raise PermissionDenied() if not frozen and election.frozen_at: raise PermissionDenied() # open for new voters check - if newvoters != None: + if newvoters is not None: if election.can_add_voters() != newvoters: raise PermissionDenied() @@ -101,10 +118,10 @@ def election_view_wrapper(request, election_uuid=None, *args, **kw): # if private election, only logged in voters if election.private_p and not checks.get('allow_logins',False): - from views import password_voter_login + from .views import password_voter_login if not user_can_see_election(request, election): return_url = request.get_full_path() - return HttpResponseRedirect("%s?%s" % (reverse(password_voter_login, args=[election.uuid]), urllib.urlencode({ + return HttpResponseRedirect("%s?%s" % (reverse(password_voter_login, args=[election.uuid]), urllib.parse.urlencode({ 'return_url' : return_url }))) @@ -139,11 +156,13 @@ def user_can_see_election(request, election): return True # then this user has to be a voter - return (get_voter(request, user, election) != None) + return get_voter(request, user, election) is not None + def api_client_can_admin_election(api_client, election): - return election.api_client == api_client and api_client != None - + return election.api_client == api_client and api_client is not None + + # decorator for checking election admin access, and some properties of the election # frozen - is the election frozen # newvoters - does the election accept new voters diff --git a/helios/signals.py b/helios/signals.py index 4c82d2ed8..305b1d911 100644 --- a/helios/signals.py +++ b/helios/signals.py @@ -1,16 +1,21 @@ """ Helios Signals -Effectively callbacks that other apps can wait and be notified about +Effectively callbacks that other apps can wait and be notified about. + +Named arguments for each signal: + election_created : "election" + vote_cast : "user", "voter", "election", "cast_vote" + election_tallied : "election" """ import django.dispatch # when an election is created -election_created = django.dispatch.Signal(providing_args=["election"]) +election_created = django.dispatch.Signal() # when a vote is cast -vote_cast = django.dispatch.Signal(providing_args=["user", "voter", "election", "cast_vote"]) +vote_cast = django.dispatch.Signal() # when an election is tallied -election_tallied = django.dispatch.Signal(providing_args=["election"]) \ No newline at end of file +election_tallied = django.dispatch.Signal() \ No newline at end of file diff --git a/helios/south_migrations/0001_initial.py b/helios/south_migrations/0001_initial.py deleted file mode 100644 index 4fa118cc4..000000000 --- a/helios/south_migrations/0001_initial.py +++ /dev/null @@ -1,259 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Adding model 'Election' - db.create_table('helios_election', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('admin', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios_auth.User'])), - ('uuid', self.gf('django.db.models.fields.CharField')(max_length=50)), - ('short_name', self.gf('django.db.models.fields.CharField')(max_length=100)), - ('name', self.gf('django.db.models.fields.CharField')(max_length=250)), - ('description', self.gf('django.db.models.fields.TextField')()), - ('public_key', self.gf('helios_auth.jsonfield.JSONField')(null=True)), - ('private_key', self.gf('helios_auth.jsonfield.JSONField')(null=True)), - ('questions', self.gf('helios_auth.jsonfield.JSONField')(null=True)), - ('eligibility', self.gf('helios_auth.jsonfield.JSONField')(null=True)), - ('openreg', self.gf('django.db.models.fields.BooleanField')(default=False)), - ('featured_p', self.gf('django.db.models.fields.BooleanField')(default=False)), - ('use_voter_aliases', self.gf('django.db.models.fields.BooleanField')(default=False)), - ('cast_url', self.gf('django.db.models.fields.CharField')(max_length=500)), - ('created_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - ('modified_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - ('frozen_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)), - ('archived_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)), - ('registration_starts_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)), - ('voting_starts_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)), - ('voting_ends_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)), - ('tallying_starts_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)), - ('voting_started_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)), - ('voting_extended_until', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)), - ('voting_ended_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)), - ('tallying_started_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)), - ('tallying_finished_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)), - ('tallies_combined_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)), - ('voters_hash', self.gf('django.db.models.fields.CharField')(max_length=100, null=True)), - ('encrypted_tally', self.gf('helios_auth.jsonfield.JSONField')(null=True)), - ('result', self.gf('helios_auth.jsonfield.JSONField')(null=True)), - ('result_proof', self.gf('helios_auth.jsonfield.JSONField')(null=True)), - )) - db.send_create_signal('helios', ['Election']) - - # Adding model 'ElectionLog' - db.create_table('helios_electionlog', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('election', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios.Election'])), - ('log', self.gf('django.db.models.fields.CharField')(max_length=500)), - ('at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - )) - db.send_create_signal('helios', ['ElectionLog']) - - # Adding model 'VoterFile' - db.create_table('helios_voterfile', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('election', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios.Election'])), - ('voter_file', self.gf('django.db.models.fields.files.FileField')(max_length=250)), - ('uploaded_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - ('processing_started_at', self.gf('django.db.models.fields.DateTimeField')(null=True)), - ('processing_finished_at', self.gf('django.db.models.fields.DateTimeField')(null=True)), - ('num_voters', self.gf('django.db.models.fields.IntegerField')(null=True)), - )) - db.send_create_signal('helios', ['VoterFile']) - - # Adding model 'Voter' - db.create_table('helios_voter', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('election', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios.Election'])), - ('name', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)), - ('voter_type', self.gf('django.db.models.fields.CharField')(max_length=100)), - ('voter_id', self.gf('django.db.models.fields.CharField')(max_length=100)), - ('uuid', self.gf('django.db.models.fields.CharField')(max_length=50)), - ('alias', self.gf('django.db.models.fields.CharField')(max_length=100, null=True)), - ('vote', self.gf('helios_auth.jsonfield.JSONField')(null=True)), - ('vote_hash', self.gf('django.db.models.fields.CharField')(max_length=100, null=True)), - ('cast_at', self.gf('django.db.models.fields.DateTimeField')(null=True)), - )) - db.send_create_signal('helios', ['Voter']) - - # Adding model 'CastVote' - db.create_table('helios_castvote', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('voter', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios.Voter'])), - ('vote', self.gf('helios_auth.jsonfield.JSONField')()), - ('vote_hash', self.gf('django.db.models.fields.CharField')(max_length=100)), - ('cast_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - ('verified_at', self.gf('django.db.models.fields.DateTimeField')(null=True)), - ('invalidated_at', self.gf('django.db.models.fields.DateTimeField')(null=True)), - )) - db.send_create_signal('helios', ['CastVote']) - - # Adding model 'AuditedBallot' - db.create_table('helios_auditedballot', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('election', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios.Election'])), - ('raw_vote', self.gf('django.db.models.fields.TextField')()), - ('vote_hash', self.gf('django.db.models.fields.CharField')(max_length=100)), - ('added_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - )) - db.send_create_signal('helios', ['AuditedBallot']) - - # Adding model 'Trustee' - db.create_table('helios_trustee', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('election', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios.Election'])), - ('uuid', self.gf('django.db.models.fields.CharField')(max_length=50)), - ('name', self.gf('django.db.models.fields.CharField')(max_length=200)), - ('email', self.gf('django.db.models.fields.EmailField')(max_length=75)), - ('secret', self.gf('django.db.models.fields.CharField')(max_length=100)), - ('public_key', self.gf('helios_auth.jsonfield.JSONField')(null=True)), - ('public_key_hash', self.gf('django.db.models.fields.CharField')(max_length=100)), - ('secret_key', self.gf('helios_auth.jsonfield.JSONField')(null=True)), - ('pok', self.gf('helios_auth.jsonfield.JSONField')(null=True)), - ('decryption_factors', self.gf('helios_auth.jsonfield.JSONField')(null=True)), - ('decryption_proofs', self.gf('helios_auth.jsonfield.JSONField')(null=True)), - )) - db.send_create_signal('helios', ['Trustee']) - - - def backwards(self, orm): - - # Deleting model 'Election' - db.delete_table('helios_election') - - # Deleting model 'ElectionLog' - db.delete_table('helios_electionlog') - - # Deleting model 'VoterFile' - db.delete_table('helios_voterfile') - - # Deleting model 'Voter' - db.delete_table('helios_voter') - - # Deleting model 'CastVote' - db.delete_table('helios_castvote') - - # Deleting model 'AuditedBallot' - db.delete_table('helios_auditedballot') - - # Deleting model 'Trustee' - db.delete_table('helios_trustee') - - - models = { - 'helios_auth.user': { - 'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'}, - 'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'info': ('helios_auth.jsonfield.JSONField', [], {}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.auditedballot': { - 'Meta': {'object_name': 'AuditedBallot'}, - 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'raw_vote': ('django.db.models.fields.TextField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.castvote': { - 'Meta': {'object_name': 'CastVote'}, - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'vote': ('helios_auth.jsonfield.JSONField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"}) - }, - 'helios.election': { - 'Meta': {'object_name': 'Election'}, - 'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}), - 'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}), - 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'description': ('django.db.models.fields.TextField', [], {}), - 'eligibility': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'encrypted_tally': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), - 'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'private_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'questions': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'result': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}) - }, - 'helios.electionlog': { - 'Meta': {'object_name': 'ElectionLog'}, - 'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'log': ('django.db.models.fields.CharField', [], {'max_length': '500'}) - }, - 'helios.trustee': { - 'Meta': {'object_name': 'Trustee'}, - 'decryption_factors': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'decryption_proofs': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), - 'pok': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.voter': { - 'Meta': {'object_name': 'Voter'}, - 'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'vote': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'voter_type': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.voterfile': { - 'Meta': {'object_name': 'VoterFile'}, - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), - 'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250'}) - } - } - - complete_apps = ['helios'] diff --git a/helios/south_migrations/0002_v3_1_new_election_and_voter_fields.py b/helios/south_migrations/0002_v3_1_new_election_and_voter_fields.py deleted file mode 100644 index a5d4e1d35..000000000 --- a/helios/south_migrations/0002_v3_1_new_election_and_voter_fields.py +++ /dev/null @@ -1,196 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Adding field 'Voter.user' - db.add_column('helios_voter', 'user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios_auth.User'], null=True), keep_default=False) - - # Adding field 'Voter.voter_login_id' - db.add_column('helios_voter', 'voter_login_id', self.gf('django.db.models.fields.CharField')(max_length=100, null=True), keep_default=False) - - # Adding field 'Voter.voter_password' - db.add_column('helios_voter', 'voter_password', self.gf('django.db.models.fields.CharField')(max_length=100, null=True), keep_default=False) - - # Adding field 'Voter.voter_name' - db.add_column('helios_voter', 'voter_name', self.gf('django.db.models.fields.CharField')(max_length=200, null=True), keep_default=False) - - # Adding field 'Voter.voter_email' - db.add_column('helios_voter', 'voter_email', self.gf('django.db.models.fields.CharField')(max_length=250, null=True), keep_default=False) - - # Adding field 'Election.datatype' - db.add_column('helios_election', 'datatype', self.gf('django.db.models.fields.CharField')(default='legacy/Election', max_length=250), keep_default=False) - - # Adding field 'Election.election_type' - db.add_column('helios_election', 'election_type', self.gf('django.db.models.fields.CharField')(default='election', max_length=250), keep_default=False) - - # Adding field 'Election.private_p' - db.add_column('helios_election', 'private_p', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False) - - # Adding field 'Election.use_advanced_audit_features' - db.add_column('helios_election', 'use_advanced_audit_features', self.gf('django.db.models.fields.BooleanField')(default=True), keep_default=False) - - # Adding field 'CastVote.vote_tinyhash' - db.add_column('helios_castvote', 'vote_tinyhash', self.gf('django.db.models.fields.CharField')(max_length=50, unique=True, null=True), keep_default=False) - - - def backwards(self, orm): - - # Deleting field 'Voter.user' - db.delete_column('helios_voter', 'user_id') - - # Deleting field 'Voter.voter_login_id' - db.delete_column('helios_voter', 'voter_login_id') - - # Deleting field 'Voter.voter_password' - db.delete_column('helios_voter', 'voter_password') - - # Deleting field 'Voter.voter_name' - db.delete_column('helios_voter', 'voter_name') - - # Deleting field 'Voter.voter_email' - db.delete_column('helios_voter', 'voter_email') - - # Deleting field 'Election.datatype' - db.delete_column('helios_election', 'datatype') - - # Deleting field 'Election.election_type' - db.delete_column('helios_election', 'election_type') - - # Deleting field 'Election.private_p' - db.delete_column('helios_election', 'private_p') - - # Deleting field 'Election.use_advanced_audit_features' - db.delete_column('helios_election', 'use_advanced_audit_features') - - # Deleting field 'CastVote.vote_tinyhash' - db.delete_column('helios_castvote', 'vote_tinyhash') - - - models = { - 'helios_auth.user': { - 'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'}, - 'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'info': ('helios_auth.jsonfield.JSONField', [], {}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.auditedballot': { - 'Meta': {'object_name': 'AuditedBallot'}, - 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'raw_vote': ('django.db.models.fields.TextField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.castvote': { - 'Meta': {'object_name': 'CastVote'}, - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'vote': ('helios_auth.jsonfield.JSONField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}), - 'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"}) - }, - 'helios.election': { - 'Meta': {'object_name': 'Election'}, - 'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}), - 'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}), - 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}), - 'description': ('django.db.models.fields.TextField', [], {}), - 'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}), - 'eligibility': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'encrypted_tally': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), - 'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'private_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'questions': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'result': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}) - }, - 'helios.electionlog': { - 'Meta': {'object_name': 'ElectionLog'}, - 'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'log': ('django.db.models.fields.CharField', [], {'max_length': '500'}) - }, - 'helios.trustee': { - 'Meta': {'object_name': 'Trustee'}, - 'decryption_factors': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'decryption_proofs': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), - 'pok': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.voter': { - 'Meta': {'object_name': 'Voter'}, - 'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'vote': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}), - 'voter_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_type': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.voterfile': { - 'Meta': {'object_name': 'VoterFile'}, - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), - 'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250'}) - } - } - - complete_apps = ['helios'] diff --git a/helios/south_migrations/0003_v3_1_election_specific_voters_with_passwords.py b/helios/south_migrations/0003_v3_1_election_specific_voters_with_passwords.py deleted file mode 100644 index 573279f7c..000000000 --- a/helios/south_migrations/0003_v3_1_election_specific_voters_with_passwords.py +++ /dev/null @@ -1,178 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import DataMigration -from django.db import models - -class Migration(DataMigration): - - def forwards(self, orm): - """ - update the voters data objects to point to users when it makes sense, - and otherwise to copy the data needed from the users table. - make all elections legacy, because before now they are. - """ - for e in orm.Election.objects.all(): - e.datatype = 'legacy/Election' - e.save() - - # use the .iterator() call to reduce caching and make this more efficient - # so as not to trigger a memory error - for v in orm.Voter.objects.all().iterator(): - user = orm['helios_auth.User'].objects.get(user_type = v.voter_type, user_id = v.voter_id) - - if v.voter_type == 'password': - v.voter_login_id = v.voter_id - v.voter_name = v.name - - v.voter_email = user.info['email'] - v.voter_password = user.info['password'] - else: - v.user = user - - v.save() - - # also, update tinyhash for all votes - for cv in orm.CastVote.objects.all().iterator(): - safe_hash = cv.vote_hash - for c in ['/', '+']: - safe_hash = safe_hash.replace(c,'') - - length = 8 - while True: - vote_tinyhash = safe_hash[:length] - if orm.CastVote.objects.filter(vote_tinyhash = vote_tinyhash).count() == 0: - break - length += 1 - - cv.vote_tinyhash = vote_tinyhash - cv.save() - - - def backwards(self, orm): - "Write your backwards methods here." - raise Exception("can't revert to system-wide user passwords, rather than election specific") - - - models = { - 'helios_auth.user': { - 'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'}, - 'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'info': ('helios_auth.jsonfield.JSONField', [], {}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.auditedballot': { - 'Meta': {'object_name': 'AuditedBallot'}, - 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'raw_vote': ('django.db.models.fields.TextField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.castvote': { - 'Meta': {'object_name': 'CastVote'}, - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'vote': ('helios_auth.jsonfield.JSONField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}), - 'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"}) - }, - 'helios.election': { - 'Meta': {'object_name': 'Election'}, - 'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}), - 'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}), - 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}), - 'description': ('django.db.models.fields.TextField', [], {}), - 'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}), - 'eligibility': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'encrypted_tally': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), - 'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'private_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'questions': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'result': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}) - }, - 'helios.electionlog': { - 'Meta': {'object_name': 'ElectionLog'}, - 'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'log': ('django.db.models.fields.CharField', [], {'max_length': '500'}) - }, - 'helios.trustee': { - 'Meta': {'object_name': 'Trustee'}, - 'decryption_factors': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'decryption_proofs': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), - 'pok': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.voter': { - 'Meta': {'object_name': 'Voter'}, - 'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'vote': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}), - 'voter_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_type': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.voterfile': { - 'Meta': {'object_name': 'VoterFile'}, - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), - 'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250'}) - } - } - - complete_apps = ['helios'] diff --git a/helios/south_migrations/0004_v3_1_remove_voter_fields.py b/helios/south_migrations/0004_v3_1_remove_voter_fields.py deleted file mode 100644 index b934e945a..000000000 --- a/helios/south_migrations/0004_v3_1_remove_voter_fields.py +++ /dev/null @@ -1,153 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Deleting field 'Voter.name' - db.delete_column('helios_voter', 'name') - - # Deleting field 'Voter.voter_id' - db.delete_column('helios_voter', 'voter_id') - - # Deleting field 'Voter.voter_type' - db.delete_column('helios_voter', 'voter_type') - - - def backwards(self, orm): - - # Adding field 'Voter.name' - db.add_column('helios_voter', 'name', self.gf('django.db.models.fields.CharField')(max_length=200, null=True), keep_default=False) - - # We cannot add back in field 'Voter.voter_id' - raise RuntimeError( - "Cannot reverse this migration. 'Voter.voter_id' and its values cannot be restored.") - - # We cannot add back in field 'Voter.voter_type' - raise RuntimeError( - "Cannot reverse this migration. 'Voter.voter_type' and its values cannot be restored.") - - - models = { - 'helios_auth.user': { - 'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'}, - 'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'info': ('helios_auth.jsonfield.JSONField', [], {}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.auditedballot': { - 'Meta': {'object_name': 'AuditedBallot'}, - 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'raw_vote': ('django.db.models.fields.TextField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.castvote': { - 'Meta': {'object_name': 'CastVote'}, - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'vote': ('helios_auth.jsonfield.JSONField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}), - 'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"}) - }, - 'helios.election': { - 'Meta': {'object_name': 'Election'}, - 'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}), - 'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}), - 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}), - 'description': ('django.db.models.fields.TextField', [], {}), - 'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}), - 'eligibility': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'encrypted_tally': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), - 'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'private_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'questions': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'result': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}) - }, - 'helios.electionlog': { - 'Meta': {'object_name': 'ElectionLog'}, - 'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'log': ('django.db.models.fields.CharField', [], {'max_length': '500'}) - }, - 'helios.trustee': { - 'Meta': {'object_name': 'Trustee'}, - 'decryption_factors': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'decryption_proofs': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), - 'pok': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.voter': { - 'Meta': {'object_name': 'Voter'}, - 'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'vote': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}), - 'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}) - }, - 'helios.voterfile': { - 'Meta': {'object_name': 'VoterFile'}, - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), - 'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250'}) - } - } - - complete_apps = ['helios'] diff --git a/helios/south_migrations/0005_add_quarantine_fields.py b/helios/south_migrations/0005_add_quarantine_fields.py deleted file mode 100644 index b0f9f1dcc..000000000 --- a/helios/south_migrations/0005_add_quarantine_fields.py +++ /dev/null @@ -1,154 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Adding field 'Election.complaint_period_ends_at' - db.add_column('helios_election', 'complaint_period_ends_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True), keep_default=False) - - # Adding field 'CastVote.quarantined_p' - db.add_column('helios_castvote', 'quarantined_p', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False) - - # Adding field 'CastVote.released_from_quarantine_at' - db.add_column('helios_castvote', 'released_from_quarantine_at', self.gf('django.db.models.fields.DateTimeField')(null=True), keep_default=False) - - - def backwards(self, orm): - - # Deleting field 'Election.complaint_period_ends_at' - db.delete_column('helios_election', 'complaint_period_ends_at') - - # Deleting field 'CastVote.quarantined_p' - db.delete_column('helios_castvote', 'quarantined_p') - - # Deleting field 'CastVote.released_from_quarantine_at' - db.delete_column('helios_castvote', 'released_from_quarantine_at') - - - models = { - 'helios_auth.user': { - 'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'}, - 'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'info': ('helios_auth.jsonfield.JSONField', [], {}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.auditedballot': { - 'Meta': {'object_name': 'AuditedBallot'}, - 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'raw_vote': ('django.db.models.fields.TextField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.castvote': { - 'Meta': {'object_name': 'CastVote'}, - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'vote': ('helios_auth.jsonfield.JSONField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}), - 'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"}) - }, - 'helios.election': { - 'Meta': {'object_name': 'Election'}, - 'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}), - 'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}), - 'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}), - 'description': ('django.db.models.fields.TextField', [], {}), - 'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}), - 'eligibility': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'encrypted_tally': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), - 'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'private_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'questions': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'result': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}) - }, - 'helios.electionlog': { - 'Meta': {'object_name': 'ElectionLog'}, - 'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'log': ('django.db.models.fields.CharField', [], {'max_length': '500'}) - }, - 'helios.trustee': { - 'Meta': {'object_name': 'Trustee'}, - 'decryption_factors': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'decryption_proofs': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), - 'pok': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.voter': { - 'Meta': {'object_name': 'Voter'}, - 'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'vote': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}), - 'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}) - }, - 'helios.voterfile': { - 'Meta': {'object_name': 'VoterFile'}, - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), - 'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250'}) - } - } - - complete_apps = ['helios'] diff --git a/helios/south_migrations/0006_auto__chg_field_voter_vote__add_unique_voter_voter_login_id_election__.py b/helios/south_migrations/0006_auto__chg_field_voter_vote__add_unique_voter_voter_login_id_election__.py deleted file mode 100644 index 2791e493a..000000000 --- a/helios/south_migrations/0006_auto__chg_field_voter_vote__add_unique_voter_voter_login_id_election__.py +++ /dev/null @@ -1,220 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Changing field 'Voter.vote' - db.alter_column('helios_voter', 'vote', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True)) - - # Adding unique constraint on 'Voter', fields ['voter_login_id', 'election'] - db.create_unique('helios_voter', ['voter_login_id', 'election_id']) - - # Changing field 'Election.result' - db.alter_column('helios_election', 'result', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True)) - - # Changing field 'Election.questions' - db.alter_column('helios_election', 'questions', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True)) - - # Changing field 'Election.encrypted_tally' - db.alter_column('helios_election', 'encrypted_tally', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True)) - - # Changing field 'Election.eligibility' - db.alter_column('helios_election', 'eligibility', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True)) - - # Changing field 'Election.private_key' - db.alter_column('helios_election', 'private_key', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True)) - - # Changing field 'Election.public_key' - db.alter_column('helios_election', 'public_key', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True)) - - # Changing field 'Trustee.public_key' - db.alter_column('helios_trustee', 'public_key', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True)) - - # Changing field 'Trustee.decryption_proofs' - db.alter_column('helios_trustee', 'decryption_proofs', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True)) - - # Changing field 'Trustee.pok' - db.alter_column('helios_trustee', 'pok', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True)) - - # Changing field 'Trustee.secret_key' - db.alter_column('helios_trustee', 'secret_key', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True)) - - # Changing field 'Trustee.decryption_factors' - db.alter_column('helios_trustee', 'decryption_factors', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True)) - - # Changing field 'CastVote.vote' - db.alter_column('helios_castvote', 'vote', self.gf('helios.datatypes.djangofield.LDObjectField')()) - - - def backwards(self, orm): - - # Removing unique constraint on 'Voter', fields ['voter_login_id', 'election'] - db.delete_unique('helios_voter', ['voter_login_id', 'election_id']) - - # Changing field 'Voter.vote' - db.alter_column('helios_voter', 'vote', self.gf('helios_auth.jsonfield.JSONField')(null=True)) - - # Changing field 'Election.result' - db.alter_column('helios_election', 'result', self.gf('helios_auth.jsonfield.JSONField')(null=True)) - - # Changing field 'Election.questions' - db.alter_column('helios_election', 'questions', self.gf('helios_auth.jsonfield.JSONField')(null=True)) - - # Changing field 'Election.encrypted_tally' - db.alter_column('helios_election', 'encrypted_tally', self.gf('helios_auth.jsonfield.JSONField')(null=True)) - - # Changing field 'Election.eligibility' - db.alter_column('helios_election', 'eligibility', self.gf('helios_auth.jsonfield.JSONField')(null=True)) - - # Changing field 'Election.private_key' - db.alter_column('helios_election', 'private_key', self.gf('helios_auth.jsonfield.JSONField')(null=True)) - - # Changing field 'Election.public_key' - db.alter_column('helios_election', 'public_key', self.gf('helios_auth.jsonfield.JSONField')(null=True)) - - # Changing field 'Trustee.public_key' - db.alter_column('helios_trustee', 'public_key', self.gf('helios_auth.jsonfield.JSONField')(null=True)) - - # Changing field 'Trustee.decryption_proofs' - db.alter_column('helios_trustee', 'decryption_proofs', self.gf('helios_auth.jsonfield.JSONField')(null=True)) - - # Changing field 'Trustee.pok' - db.alter_column('helios_trustee', 'pok', self.gf('helios_auth.jsonfield.JSONField')(null=True)) - - # Changing field 'Trustee.secret_key' - db.alter_column('helios_trustee', 'secret_key', self.gf('helios_auth.jsonfield.JSONField')(null=True)) - - # Changing field 'Trustee.decryption_factors' - db.alter_column('helios_trustee', 'decryption_factors', self.gf('helios_auth.jsonfield.JSONField')(null=True)) - - # Changing field 'CastVote.vote' - db.alter_column('helios_castvote', 'vote', self.gf('helios_auth.jsonfield.JSONField')()) - - - models = { - 'helios_auth.user': { - 'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'}, - 'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'info': ('helios_auth.jsonfield.JSONField', [], {}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.auditedballot': { - 'Meta': {'object_name': 'AuditedBallot'}, - 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'raw_vote': ('django.db.models.fields.TextField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.castvote': { - 'Meta': {'object_name': 'CastVote'}, - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}), - 'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"}) - }, - 'helios.election': { - 'Meta': {'object_name': 'Election'}, - 'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}), - 'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}), - 'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}), - 'description': ('django.db.models.fields.TextField', [], {}), - 'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}), - 'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), - 'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}) - }, - 'helios.electionlog': { - 'Meta': {'object_name': 'ElectionLog'}, - 'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'log': ('django.db.models.fields.CharField', [], {'max_length': '500'}) - }, - 'helios.trustee': { - 'Meta': {'object_name': 'Trustee'}, - 'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), - 'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.voter': { - 'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'}, - 'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}), - 'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}) - }, - 'helios.voterfile': { - 'Meta': {'object_name': 'VoterFile'}, - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), - 'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250'}) - } - } - - complete_apps = ['helios'] diff --git a/helios/south_migrations/0007_auto__add_field_voterfile_voter_file_content__chg_field_voterfile_vote.py b/helios/south_migrations/0007_auto__add_field_voterfile_voter_file_content__chg_field_voterfile_vote.py deleted file mode 100644 index 04a7f00b0..000000000 --- a/helios/south_migrations/0007_auto__add_field_voterfile_voter_file_content__chg_field_voterfile_vote.py +++ /dev/null @@ -1,149 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Adding field 'VoterFile.voter_file_content' - db.add_column('helios_voterfile', 'voter_file_content', self.gf('django.db.models.fields.TextField')(null=True), keep_default=False) - - # Changing field 'VoterFile.voter_file' - db.alter_column('helios_voterfile', 'voter_file', self.gf('django.db.models.fields.files.FileField')(max_length=250, null=True)) - - - def backwards(self, orm): - - # Deleting field 'VoterFile.voter_file_content' - db.delete_column('helios_voterfile', 'voter_file_content') - - # User chose to not deal with backwards NULL issues for 'VoterFile.voter_file' - raise RuntimeError("Cannot reverse this migration. 'VoterFile.voter_file' and its values cannot be restored.") - - - models = { - 'helios_auth.user': { - 'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'}, - 'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'info': ('helios_auth.jsonfield.JSONField', [], {}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.auditedballot': { - 'Meta': {'object_name': 'AuditedBallot'}, - 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'raw_vote': ('django.db.models.fields.TextField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.castvote': { - 'Meta': {'object_name': 'CastVote'}, - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}), - 'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"}) - }, - 'helios.election': { - 'Meta': {'object_name': 'Election'}, - 'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}), - 'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}), - 'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}), - 'description': ('django.db.models.fields.TextField', [], {}), - 'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}), - 'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), - 'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}) - }, - 'helios.electionlog': { - 'Meta': {'object_name': 'ElectionLog'}, - 'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'log': ('django.db.models.fields.CharField', [], {'max_length': '500'}) - }, - 'helios.trustee': { - 'Meta': {'object_name': 'Trustee'}, - 'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), - 'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.voter': { - 'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'}, - 'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}), - 'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}) - }, - 'helios.voterfile': { - 'Meta': {'object_name': 'VoterFile'}, - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), - 'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250', 'null': 'True'}), - 'voter_file_content': ('django.db.models.fields.TextField', [], {'null': 'True'}) - } - } - - complete_apps = ['helios'] diff --git a/helios/south_migrations/0008_auto__add_unique_trustee_election_email.py b/helios/south_migrations/0008_auto__add_unique_trustee_election_email.py deleted file mode 100644 index 974dfd2db..000000000 --- a/helios/south_migrations/0008_auto__add_unique_trustee_election_email.py +++ /dev/null @@ -1,143 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Adding unique constraint on 'Trustee', fields ['election', 'email'] - db.create_unique('helios_trustee', ['election_id', 'email']) - - - def backwards(self, orm): - - # Removing unique constraint on 'Trustee', fields ['election', 'email'] - db.delete_unique('helios_trustee', ['election_id', 'email']) - - - models = { - 'helios.auditedballot': { - 'Meta': {'object_name': 'AuditedBallot'}, - 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'raw_vote': ('django.db.models.fields.TextField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.castvote': { - 'Meta': {'object_name': 'CastVote'}, - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}), - 'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"}) - }, - 'helios.election': { - 'Meta': {'object_name': 'Election'}, - 'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}), - 'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}), - 'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}), - 'description': ('django.db.models.fields.TextField', [], {}), - 'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}), - 'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), - 'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}) - }, - 'helios.electionlog': { - 'Meta': {'object_name': 'ElectionLog'}, - 'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'log': ('django.db.models.fields.CharField', [], {'max_length': '500'}) - }, - 'helios.trustee': { - 'Meta': {'unique_together': "(('election', 'email'),)", 'object_name': 'Trustee'}, - 'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), - 'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.voter': { - 'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'}, - 'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}), - 'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}) - }, - 'helios.voterfile': { - 'Meta': {'object_name': 'VoterFile'}, - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), - 'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250', 'null': 'True'}), - 'voter_file_content': ('django.db.models.fields.TextField', [], {'null': 'True'}) - }, - 'helios_auth.user': { - 'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'}, - 'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'info': ('helios_auth.jsonfield.JSONField', [], {}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - } - } - - complete_apps = ['helios'] diff --git a/helios/south_migrations/0009_auto__add_field_election_help_email.py b/helios/south_migrations/0009_auto__add_field_election_help_email.py deleted file mode 100644 index 1500f0e43..000000000 --- a/helios/south_migrations/0009_auto__add_field_election_help_email.py +++ /dev/null @@ -1,144 +0,0 @@ -# encoding: utf-8 -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Adding field 'Election.help_email' - db.add_column('helios_election', 'help_email', self.gf('django.db.models.fields.EmailField')(max_length=75, null=True), keep_default=False) - - - def backwards(self, orm): - - # Deleting field 'Election.help_email' - db.delete_column('helios_election', 'help_email') - - - models = { - 'helios.auditedballot': { - 'Meta': {'object_name': 'AuditedBallot'}, - 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'raw_vote': ('django.db.models.fields.TextField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.castvote': { - 'Meta': {'object_name': 'CastVote'}, - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}), - 'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"}) - }, - 'helios.election': { - 'Meta': {'object_name': 'Election'}, - 'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}), - 'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}), - 'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}), - 'description': ('django.db.models.fields.TextField', [], {}), - 'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}), - 'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'help_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), - 'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}) - }, - 'helios.electionlog': { - 'Meta': {'object_name': 'ElectionLog'}, - 'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'log': ('django.db.models.fields.CharField', [], {'max_length': '500'}) - }, - 'helios.trustee': { - 'Meta': {'unique_together': "(('election', 'email'),)", 'object_name': 'Trustee'}, - 'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), - 'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.voter': { - 'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'}, - 'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}), - 'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}) - }, - 'helios.voterfile': { - 'Meta': {'object_name': 'VoterFile'}, - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), - 'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250', 'null': 'True'}), - 'voter_file_content': ('django.db.models.fields.TextField', [], {'null': 'True'}) - }, - 'helios_auth.user': { - 'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'}, - 'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'info': ('helios_auth.jsonfield.JSONField', [], {}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - } - } - - complete_apps = ['helios'] diff --git a/helios/south_migrations/0010_auto__add_field_election_randomize_answer_order.py b/helios/south_migrations/0010_auto__add_field_election_randomize_answer_order.py deleted file mode 100644 index 96d1eb066..000000000 --- a/helios/south_migrations/0010_auto__add_field_election_randomize_answer_order.py +++ /dev/null @@ -1,146 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding field 'Election.randomize_answer_order' - db.add_column('helios_election', 'randomize_answer_order', - self.gf('django.db.models.fields.BooleanField')(default=False), - keep_default=False) - - - def backwards(self, orm): - # Deleting field 'Election.randomize_answer_order' - db.delete_column('helios_election', 'randomize_answer_order') - - - models = { - 'helios.auditedballot': { - 'Meta': {'object_name': 'AuditedBallot'}, - 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'raw_vote': ('django.db.models.fields.TextField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.castvote': { - 'Meta': {'object_name': 'CastVote'}, - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}), - 'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"}) - }, - 'helios.election': { - 'Meta': {'object_name': 'Election'}, - 'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}), - 'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}), - 'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}), - 'description': ('django.db.models.fields.TextField', [], {}), - 'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}), - 'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'help_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), - 'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'randomize_answer_order': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}) - }, - 'helios.electionlog': { - 'Meta': {'object_name': 'ElectionLog'}, - 'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'log': ('django.db.models.fields.CharField', [], {'max_length': '500'}) - }, - 'helios.trustee': { - 'Meta': {'unique_together': "(('election', 'email'),)", 'object_name': 'Trustee'}, - 'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), - 'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.voter': { - 'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'}, - 'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}), - 'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}) - }, - 'helios.voterfile': { - 'Meta': {'object_name': 'VoterFile'}, - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), - 'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250', 'null': 'True'}), - 'voter_file_content': ('django.db.models.fields.TextField', [], {'null': 'True'}) - }, - 'helios_auth.user': { - 'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'}, - 'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'info': ('helios_auth.jsonfield.JSONField', [], {}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - } - } - - complete_apps = ['helios'] \ No newline at end of file diff --git a/helios/south_migrations/0011_auto__add_field_election_election_info_url.py b/helios/south_migrations/0011_auto__add_field_election_election_info_url.py deleted file mode 100644 index 48c98fb14..000000000 --- a/helios/south_migrations/0011_auto__add_field_election_election_info_url.py +++ /dev/null @@ -1,147 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding field 'Election.election_info_url' - db.add_column('helios_election', 'election_info_url', - self.gf('django.db.models.fields.CharField')(max_length=300, null=True), - keep_default=False) - - - def backwards(self, orm): - # Deleting field 'Election.election_info_url' - db.delete_column('helios_election', 'election_info_url') - - - models = { - 'helios.auditedballot': { - 'Meta': {'object_name': 'AuditedBallot'}, - 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'raw_vote': ('django.db.models.fields.TextField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.castvote': { - 'Meta': {'object_name': 'CastVote'}, - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}), - 'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"}) - }, - 'helios.election': { - 'Meta': {'object_name': 'Election'}, - 'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}), - 'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}), - 'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}), - 'description': ('django.db.models.fields.TextField', [], {}), - 'election_info_url': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'}), - 'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}), - 'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'help_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), - 'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'randomize_answer_order': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}) - }, - 'helios.electionlog': { - 'Meta': {'object_name': 'ElectionLog'}, - 'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'log': ('django.db.models.fields.CharField', [], {'max_length': '500'}) - }, - 'helios.trustee': { - 'Meta': {'unique_together': "(('election', 'email'),)", 'object_name': 'Trustee'}, - 'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), - 'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.voter': { - 'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'}, - 'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}), - 'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}) - }, - 'helios.voterfile': { - 'Meta': {'object_name': 'VoterFile'}, - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), - 'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250', 'null': 'True'}), - 'voter_file_content': ('django.db.models.fields.TextField', [], {'null': 'True'}) - }, - 'helios_auth.user': { - 'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'}, - 'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'info': ('helios_auth.jsonfield.JSONField', [], {}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - } - } - - complete_apps = ['helios'] \ No newline at end of file diff --git a/helios/south_migrations/0012_auto__add_field_election_result_released_at.py b/helios/south_migrations/0012_auto__add_field_election_result_released_at.py deleted file mode 100644 index 0cc6853ee..000000000 --- a/helios/south_migrations/0012_auto__add_field_election_result_released_at.py +++ /dev/null @@ -1,148 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding field 'Election.result_released_at' - db.add_column('helios_election', 'result_released_at', - self.gf('django.db.models.fields.DateTimeField')(default=None, null=True), - keep_default=False) - - - def backwards(self, orm): - # Deleting field 'Election.result_released_at' - db.delete_column('helios_election', 'result_released_at') - - - models = { - 'helios.auditedballot': { - 'Meta': {'object_name': 'AuditedBallot'}, - 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'raw_vote': ('django.db.models.fields.TextField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'helios.castvote': { - 'Meta': {'object_name': 'CastVote'}, - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}), - 'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"}) - }, - 'helios.election': { - 'Meta': {'object_name': 'Election'}, - 'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}), - 'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}), - 'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}), - 'description': ('django.db.models.fields.TextField', [], {}), - 'election_info_url': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'}), - 'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}), - 'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'help_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}), - 'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'randomize_answer_order': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'result_released_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), - 'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}) - }, - 'helios.electionlog': { - 'Meta': {'object_name': 'ElectionLog'}, - 'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'log': ('django.db.models.fields.CharField', [], {'max_length': '500'}) - }, - 'helios.trustee': { - 'Meta': {'unique_together': "(('election', 'email'),)", 'object_name': 'Trustee'}, - 'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}), - 'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'helios.voter': { - 'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'}, - 'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}), - 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}), - 'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}), - 'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}), - 'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), - 'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}) - }, - 'helios.voterfile': { - 'Meta': {'object_name': 'VoterFile'}, - 'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), - 'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250', 'null': 'True'}), - 'voter_file_content': ('django.db.models.fields.TextField', [], {'null': 'True'}) - }, - 'helios_auth.user': { - 'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'}, - 'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'info': ('helios_auth.jsonfield.JSONField', [], {}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}), - 'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}), - 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - } - } - - complete_apps = ['helios'] \ No newline at end of file diff --git a/helios/stats_url_names.py b/helios/stats_url_names.py new file mode 100644 index 000000000..2b8ffe2cd --- /dev/null +++ b/helios/stats_url_names.py @@ -0,0 +1,5 @@ +STATS_HOME="stats@home" +STATS_FORCE_QUEUE="stats@force-queue" +STATS_ELECTIONS="stats@elections" +STATS_ELECTIONS_PROBLEMS="stats@elections-problems" +STATS_RECENT_VOTES="stats@recent-votes" diff --git a/helios/stats_urls.py b/helios/stats_urls.py index 2d26d1961..a4327a00c 100644 --- a/helios/stats_urls.py +++ b/helios/stats_urls.py @@ -4,15 +4,15 @@ Ben Adida (ben@adida.net) """ -from django.conf.urls import * +from django.urls import path -from helios.stats_views import * +from helios.stats_views import (home, force_queue, elections, recent_problem_elections, recent_votes) +import helios.stats_url_names as names -urlpatterns = patterns( - '', - (r'^$', home), - (r'^force-queue$', force_queue), - (r'^elections$', elections), - (r'^problem-elections$', recent_problem_elections), - (r'^recent-votes$', recent_votes), -) +urlpatterns = [ + path('', home, name=names.STATS_HOME), + path('force-queue', force_queue, name=names.STATS_FORCE_QUEUE), + path('elections', elections, name=names.STATS_ELECTIONS), + path('problem-elections', recent_problem_elections, name=names.STATS_ELECTIONS_PROBLEMS), + path('recent-votes', recent_votes, name=names.STATS_RECENT_VOTES), +] diff --git a/helios/stats_views.py b/helios/stats_views.py index 7d2a62ec6..e506637fa 100644 --- a/helios/stats_views.py +++ b/helios/stats_views.py @@ -2,18 +2,19 @@ Helios stats views """ -from django.core.urlresolvers import reverse -from django.core.mail import send_mail +import datetime + from django.core.paginator import Paginator -from django.http import * -from django.db import transaction -from django.db.models import * +from django.urls import reverse +from django.db.models import Max, Count +from django.http import HttpResponseRedirect -from security import * -from helios_auth.security import get_user, save_in_session_across_logouts -from view_utils import * +from helios import tasks, url_names +from helios.models import CastVote, Election +from helios_auth.security import get_user +from .security import PermissionDenied +from .view_utils import render_template -from helios import tasks def require_admin(request): user = get_user(request) @@ -33,7 +34,7 @@ def force_queue(request): for cv in votes_in_queue: tasks.cast_vote_verify_and_store.delay(cv.id) - return HttpResponseRedirect(reverse(home)) + return HttpResponseRedirect(reverse(url_names.stats.STATS_HOME)) def elections(request): user = require_admin(request) @@ -42,8 +43,7 @@ def elections(request): limit = int(request.GET.get('limit', 25)) q = request.GET.get('q','') - elections = Election.objects.filter(name__icontains = q) - elections.all().order_by('-created_at') + elections = Election.objects.filter(name__icontains = q).order_by('-created_at') elections_paginator = Paginator(elections, limit) elections_page = elections_paginator.page(page) diff --git a/helios/tasks.py b/helios/tasks.py index 5f7bba873..4aa1de432 100644 --- a/helios/tasks.py +++ b/helios/tasks.py @@ -4,20 +4,18 @@ 2010-08-01 ben@adida.net """ - -from celery.decorators import task - -from models import * -from view_utils import render_template_raw -import signals - import copy +from celery import shared_task +from celery.utils.log import get_logger + +from . import signals +from .models import CastVote, Election, Voter, VoterFile +from .view_utils import render_template_raw -from django.conf import settings -@task() +@shared_task def cast_vote_verify_and_store(cast_vote_id, status_update_message=None, **kwargs): - cast_vote = CastVote.objects.get(id = cast_vote_id) + cast_vote = CastVote.objects.get(id=cast_vote_id) result = cast_vote.verify_and_store() voter = cast_vote.voter @@ -27,23 +25,22 @@ def cast_vote_verify_and_store(cast_vote_id, status_update_message=None, **kwarg if result: # send the signal signals.vote_cast.send(sender=election, election=election, user=user, voter=voter, cast_vote=cast_vote) - - if status_update_message and user.can_update_status(): - from views import get_election_url + if status_update_message and user.can_update_status(): user.update_status(status_update_message) else: - logger = cast_vote_verify_and_store.get_logger(**kwargs) + logger = get_logger(cast_vote_verify_and_store.__name__) logger.error("Failed to verify and store %d" % cast_vote_id) - -@task() + + +@shared_task def voters_email(election_id, subject_template, body_template, extra_vars={}, voter_constraints_include=None, voter_constraints_exclude=None): """ voter_constraints_include are conditions on including voters voter_constraints_exclude are conditions on excluding voters """ - election = Election.objects.get(id = election_id) + election = Election.objects.get(id=election_id) # select the right list of voters voters = election.voter_set.all() @@ -53,61 +50,67 @@ def voters_email(election_id, subject_template, body_template, extra_vars={}, voters = voters.exclude(**voter_constraints_exclude) for voter in voters: - single_voter_email.delay(voter.uuid, subject_template, body_template, extra_vars) + single_voter_email.delay(voter.uuid, subject_template, body_template, extra_vars) -@task() + +@shared_task def voters_notify(election_id, notification_template, extra_vars={}): - election = Election.objects.get(id = election_id) + election = Election.objects.get(id=election_id) for voter in election.voter_set.all(): single_voter_notify.delay(voter.uuid, notification_template, extra_vars) -@task() + +@shared_task def single_voter_email(voter_uuid, subject_template, body_template, extra_vars={}): - voter = Voter.objects.get(uuid = voter_uuid) + voter = Voter.objects.get(uuid=voter_uuid) the_vars = copy.copy(extra_vars) - the_vars.update({'voter' : voter}) + the_vars.update({'election': voter.election}) + the_vars.update({'voter': voter}) subject = render_template_raw(None, subject_template, the_vars) body = render_template_raw(None, body_template, the_vars) voter.send_message(subject, body) -@task() + +@shared_task def single_voter_notify(voter_uuid, notification_template, extra_vars={}): - voter = Voter.objects.get(uuid = voter_uuid) + voter = Voter.objects.get(uuid=voter_uuid) the_vars = copy.copy(extra_vars) - the_vars.update({'voter' : voter}) + the_vars.update({'voter': voter}) notification = render_template_raw(None, notification_template, the_vars) voter.send_notification(notification) -@task() + +@shared_task def election_compute_tally(election_id): - election = Election.objects.get(id = election_id) + election = Election.objects.get(id=election_id) election.compute_tally() - election_notify_admin.delay(election_id = election_id, - subject = "encrypted tally computed", - body = """ + election_notify_admin.delay(election_id=election_id, + subject="encrypted tally computed", + body=""" The encrypted tally for election %s has been computed. -- Helios """ % election.name) - + if election.has_helios_trustee(): - tally_helios_decrypt.delay(election_id = election.id) + tally_helios_decrypt.delay(election_id=election.id) + -@task() +@shared_task def tally_helios_decrypt(election_id): - election = Election.objects.get(id = election_id) + election = Election.objects.get(id=election_id) election.helios_trustee_decrypt() - election_notify_admin.delay(election_id = election_id, - subject = 'Helios Decrypt', - body = """ + election_notify_admin.delay(election_id=election_id, + subject='Helios Decrypt', + body=""" Helios has decrypted its portion of the tally for election %s. @@ -115,13 +118,14 @@ def tally_helios_decrypt(election_id): Helios """ % election.name) -@task() + +@shared_task def voter_file_process(voter_file_id): - voter_file = VoterFile.objects.get(id = voter_file_id) + voter_file = VoterFile.objects.get(id=voter_file_id) voter_file.process() - election_notify_admin.delay(election_id = voter_file.election.id, - subject = 'voter file processed', - body = """ + election_notify_admin.delay(election_id=voter_file.election.id, + subject='voter file processed', + body=""" Your voter file upload for election %s has been processed. @@ -131,7 +135,8 @@ def voter_file_process(voter_file_id): Helios """ % (voter_file.election.name, voter_file.num_voters)) -@task() + +@shared_task def election_notify_admin(election_id, subject, body): - election = Election.objects.get(id = election_id) + election = Election.objects.get(id=election_id) election.admin.send_message(subject, body) diff --git a/helios/templates/_castconfirm_docast.html b/helios/templates/_castconfirm_docast.html index fa6b3e70a..9d6f6cecb 100644 --- a/helios/templates/_castconfirm_docast.html +++ b/helios/templates/_castconfirm_docast.html @@ -23,7 +23,7 @@

    -
    +
    If you cancel now, your ballot will NOT be recorded.
    You can start the voting process over again, of course.

    diff --git a/helios/templates/_castconfirm_password.html b/helios/templates/_castconfirm_password.html index 25e31de21..ebfb8e0f5 100644 --- a/helios/templates/_castconfirm_password.html +++ b/helios/templates/_castconfirm_password.html @@ -1,23 +1,28 @@ -Please provide the voter ID and password you received by email.

    - + -
    - {{password_login_form.as_table}} -
    + +
    +{{password_login_form.as_p}} {% if bad_voter_login %}

    bad voter ID or password, please try again.

    {% endif %} {% if cast_ballot == "1" %} + +

    + Your voter ID and password can be found in the email you received. + If you cannot find your login information, contact your election administrator at {{election.help_email}}. +

    -You may cast as many ballots as you wish:
    only the last one counts. +Cast as many ballots as you wish: only the last one counts.

    - {% else %} {% endif %} +
    + diff --git a/helios/templates/cast_done.html b/helios/templates/cast_done.html index 75265abb0..a007ee37c 100644 --- a/helios/templates/cast_done.html +++ b/helios/templates/cast_done.html @@ -4,23 +4,24 @@ {% block content %}

    {{election.name}} — Vote Successfully Cast!

    +{% if vote_hash %}

    - Congratulations, your vote has been successfully cast! + Your ballot tracker is:
    + {{vote_hash}}

    -

    - Your smart ballot tracker is:

    - {{vote_hash}} + This ballot tracker mathematically ensures that your ballot is counted. You can check all ballot trackers by viewing the voter list, where your ballot tracker should appear within a few minutes.

    +{% endif %} {% if logout %}

    For your safety, we have logged you out.

    - {% endif %}

    -[ return to election info ] +[ return to election info ]

    {% endblock %} diff --git a/helios/templates/castvote.html b/helios/templates/castvote.html index 1c21d73f5..21b5fa72f 100644 --- a/helios/templates/castvote.html +++ b/helios/templates/castvote.html @@ -3,7 +3,7 @@ {% block title %}{{cast_vote.vote_tinyhash}} — {{election.name}}{% endblock %} {% block content %}

    Cast Vote {{cast_vote.vote_tinyhash}}

    -cast in {{election.name}}
    +cast in {{election.name}}
    Fingerprint: {{cast_vote.vote_hash}}
    by {% if the_voter.alias %} diff --git a/helios/templates/combine_decryptions.html b/helios/templates/combine_decryptions.html index 3530867df..89d60f120 100644 --- a/helios/templates/combine_decryptions.html +++ b/helios/templates/combine_decryptions.html @@ -2,7 +2,7 @@ {% block title %}Compute Tally — {{election.name}}{% endblock %} {% block content %} -

    {{election.name}} — Compute Tally [cancel]

    +

    {{election.name}} — Compute Tally [cancel]

    You are about to compute the tally for this election. You only will then see the results. diff --git a/helios/templates/election_audited_ballots.html b/helios/templates/election_audited_ballots.html index 2e01f9b32..3bc29d396 100644 --- a/helios/templates/election_audited_ballots.html +++ b/helios/templates/election_audited_ballots.html @@ -3,10 +3,10 @@ {% block title %}Audited Ballots for {{election.name}}{% endblock %} {% block content %} -

    {{election.name}} — Audited Ballots [back to election]

    +

    {{election.name}} — Audited Ballots [back to election]

    -When you prepare a ballot with Helios, you immediately receive a smart ballot tracker. Before you choose to cast that ballot, you have the option to ask Helios to "break open" that encrypted ballot and verify that Helios encrypted your ballot correctly. Once that's done, you can post that opened ballot here, on the audited ballots' list, for everyone to verify (your identity is not included). Once you've done this, you have to re-encrypt your choices and obtain a different smart ballot tracker. This helps reduce the chance that someone might coerce you to vote differently from your true choice. +When you prepare a ballot with Helios, you immediately receive a ballot tracker. Before you choose to cast that ballot, you have the option to ask Helios to "break open" that encrypted ballot and verify that Helios encrypted your ballot correctly. Once that's done, you can post that opened ballot here, on the audited ballots' list, for everyone to verify (your identity is not included). Once you've done this, you have to re-encrypt your choices and obtain a different ballot tracker. This helps reduce the chance that someone might coerce you to vote differently from your true choice.

    @@ -14,7 +14,7 @@

    {{election.name}} — Audited Ballots single ballot verifier. + To verify an audited ballot, copy its entire content and paste it in the single ballot verifier.

    {% if audited_ballots %} @@ -26,10 +26,10 @@

    {{election.name}} — Audited Ballots next {{limit}}    {% endif %} -{% ifequal offset 0 %} +{% if offset == 0 %} {% else %} back to start    -{% endifequal %} +{% endif %} {% if more_p %} next {{limit}} {% endif %} diff --git a/helios/templates/election_bboard.html b/helios/templates/election_bboard.html index a5fd7e7d9..5acd865af 100644 --- a/helios/templates/election_bboard.html +++ b/helios/templates/election_bboard.html @@ -3,7 +3,7 @@ {% block title %}Ballot Tracking Center for {{election.name}}{% endblock %} {% block content %} -

    {{election.name}} — Ballot Tracking Center [back to election]

    +

    {{election.name}} — Ballot Tracking Center [back to election]

    This is the ballot tracking center, which displays the tracking numbers of all cast ballots in this election. @@ -16,15 +16,15 @@

    {{election.name}} — Ballot Tracking Center next {{limit}}    +next {{limit}}    {% endif %} -{% ifequal offset 0 %} +{% if offset == 0 %} {% else %} -back to start    -{% endifequal %} +back to start    +{% endif %} {% if more_p %} -next {{limit}} +next {{limit}} {% endif %} +{% endif %} {% endfor %}
    @@ -40,7 +40,7 @@

    {{election.name}} — Ballot Tracking Center {% if voter.name %}{{voter.name}}{% else %}{{voter.voter_id}}{% endif %} -{% endif %}

    {% if voter.vote_hash %}{{voter.vote_hash}} [view]{% else %}—{% endif %}
    {% if voter.vote_hash %}{{voter.vote_hash}} [view]{% else %}—{% endif %}
    diff --git a/helios/templates/election_build.html b/helios/templates/election_build.html index 4526d6a05..8f56185fc 100644 --- a/helios/templates/election_build.html +++ b/helios/templates/election_build.html @@ -1,7 +1,7 @@ {% extends "helios/templates/cryptobase.html" %} {% block content %} -

    {{election.name}} — Questions [back to election]

    +

    {{election.name}} — Questions [back to election]

    -

    Submitting your vote for {{election.name}}...

    +

    Don't go yet!
    Your ballot has not yet been cast.

    Verifying and Casting your ballot
    +
    +Your ballot tracker is:
    +{{vote_fingerprint}} +

    +
    +
    {% if voter %} @@ -50,13 +56,6 @@ {% else %} {% if show_password %} -{% if user %} -

    Wait!

    -

    -You are logged in as {{user.display_html_small|safe}}, but this election
    -requires election-specific credentials. -

    -{% endif %} {% with cast_ballot="1" %} {% include "_castconfirm_password.html" %} {% endwith %} @@ -73,11 +72,10 @@

    Sorry!

    {% endif %}

    - [return to the main election page] + [return to the main election page]

    {% else %}

    -

    Wait! You need to log in.

    {% if election.openreg %} {% if election.eligibility %} @@ -98,9 +96,6 @@

    Wait! You need to log in.

    {% endif %} {% endif %} -
    -Your smart ballot tracker is:

    - {{vote_fingerprint}}

    {# this closes the IF ELSE of this being password_only #} {% endif %} diff --git a/helios/templates/election_compute_tally.html b/helios/templates/election_compute_tally.html index fd98440e3..2ad050a02 100644 --- a/helios/templates/election_compute_tally.html +++ b/helios/templates/election_compute_tally.html @@ -19,12 +19,12 @@

    Compute Tally for Election: {{election.name}}

    - + {% else %}

    No votes have been cast in this election. At least one vote must be cast before you compute the tally.

    -back to election +back to election

    {% endif %}
    diff --git a/helios/templates/election_edit.html b/helios/templates/election_edit.html index 2519fb704..025b25855 100644 --- a/helios/templates/election_edit.html +++ b/helios/templates/election_edit.html @@ -2,7 +2,7 @@ {% block content %} -

    {{election.name}} — Update [cancel]

    +

    {{election.name}} — Update [cancel]

    {% if error %}

    diff --git a/helios/templates/election_extend.html b/helios/templates/election_extend.html index 0db2c448b..813afe7fd 100644 --- a/helios/templates/election_extend.html +++ b/helios/templates/election_extend.html @@ -2,7 +2,7 @@ {% block content %} -

    {{election.name}} — Extend Voting [cancel]

    +

    {{election.name}} — Extend Voting [cancel]

    diff --git a/helios/templates/election_freeze.html b/helios/templates/election_freeze.html index a0aad4dd0..dcf49c861 100644 --- a/helios/templates/election_freeze.html +++ b/helios/templates/election_freeze.html @@ -3,8 +3,7 @@ {% block content %}

    {{election.name}} — Freeze Ballot

    -Once the ballot is frozen, the questions and options can no longer be modified.
    -The list of trustees and their public keys will also be frozen. +Once an election is frozen, the questions, options, and trustees can no longer be modified.

    @@ -29,14 +28,14 @@

    {{election.name}} — Freeze Ballot

  • {{issue.action}}
  • {% endfor %}
- go back to the election + go back to the election

{% else %} - + {% endif %} diff --git a/helios/templates/election_keygenerator.html b/helios/templates/election_keygenerator.html index 60a2c55c7..bce34b875 100644 --- a/helios/templates/election_keygenerator.html +++ b/helios/templates/election_keygenerator.html @@ -35,7 +35,7 @@ $('#generator').hide(); // get some more server-side randomness for keygen - $.getJSON('../../get-randomness', function(result) { + $.getJSON('{% url "election@get-randomness" election.uuid %}', function(result) { sjcl.random.addEntropy(result.randomness); BigInt.setup(function() { ELGAMAL_PARAMS = ElGamal.Params.fromJSONObject({{eg_params_json|safe}}); @@ -76,11 +76,23 @@ } function download_sk() { - UTILS.open_window_with_content(jQuery.toJSON(SECRET_KEY), "application/json"); + $('#pk_content').show(); + $('#sk_content').html(jQuery.toJSON(SECRET_KEY)); +} + +function download_sk_to_file(filename) { + var element = document.createElement('a'); + element.setAttribute('href','data:text/plain;charset=utf-8,'+ encodeURIComponent(jQuery.toJSON(SECRET_KEY))); + element.setAttribute('download', filename); + element.style.display = 'none'; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); } function show_pk() { $('#sk_download').hide(); + $('#pk_content').hide(); $('#pk_hash').show(); $('#pk_form').show(); } @@ -101,7 +113,7 @@

{{election.name}} — Trustee {{trustee.name}} — Key -

+
If you've already generated a keypair, you can reuse it.

@@ -126,15 +138,28 @@

Your Secret Key

- +

+ - + + -
+

Your Public Key

It's time to upload the public key to the server. diff --git a/helios/templates/election_new_2.html b/helios/templates/election_new_2.html index 16061c2d0..9486ecc3a 100644 --- a/helios/templates/election_new_2.html +++ b/helios/templates/election_new_2.html @@ -6,11 +6,11 @@ var SECRET_KEY; function before_create() { -{% ifequal election_type "one" %} +{% if election_type == "one" %} return confirm('Have you made sure to copy the private key to a safe place?\n\nOnce you click OK, Helios will not be able to recover the secret key without your help!'); {% else %} return true; -{% endifequal %} +{% endif %} } function generate_keypair() { @@ -26,43 +26,43 @@ $('#pk').val(jQuery.toJSON(SECRET_KEY.pk)); -{% ifequal election_type "one" %} +{% if election_type == "one" %} $('#sk_textarea').val(jQuery.toJSON(SECRET_KEY)); $('#sk_form').show(); {% else %} -{% ifequal election_type "helios" %} +{% if election_type == "helios" %} $('#sk').val(jQuery.toJSON(SECRET_KEY)); -{% endifequal %} -{% endifequal %} +{% endif %} +{% endif %} $('#submit').show(); } $(document).ready(function() { -{% ifnotequal election_type "multiple" %} +{% if election_type != "multiple" %} $('#submit').hide(); -{% endifnotequal %} +{% endif %} $('#sk_form').hide(); });

Create a New Election: {{name}}

-{% ifequal election_type "helios" %} +{% if election_type == "helios" %} An election managed by Helios. {% else %} -{% ifequal election_type "one" %} +{% if election_type == "one" %} An election managed by you, the single administrator. {% else %} An election managed by multiple trustees. -{% endifequal %} -{% endifequal %} +{% endif %} +{% endif %}
-{% ifnotequal election_type "multiple" %} +{% if election_type != "multiple" %} {% else %}
@@ -73,12 +73,12 @@

Trustees (up to 5)



-{% endifnotequal %} +{% endif %}



-{% ifequal election_type "one" %} +{% if election_type == "one" %}
Your Election's Secret Key:
@@ -88,5 +88,5 @@

Trustees (up to 5)

(You need to copy and paste this key and keep it safe,
otherwise you won't be able to tally your election.)
-{% endifequal %} +{% endif %} {% endblock %} \ No newline at end of file diff --git a/helios/templates/election_not_started.html b/helios/templates/election_not_started.html index 4ba944e1a..595b9d1e8 100644 --- a/helios/templates/election_not_started.html +++ b/helios/templates/election_not_started.html @@ -5,10 +5,15 @@

Election {{election.name}} Not Yet Open

- This election is not yet open. You probably got here from the Ballot Preview. + This election is not yet open.

- back to the election + {% if election.voting_start_at %}Voting start at {{election.voting_start_at}}
{% endif %} + {% if election.voting_end_at %}Voting end at {{election.voting_end_at}}
{% endif %} +

+ +

+ back to the election

{% endblock %} diff --git a/helios/templates/election_questions.html b/helios/templates/election_questions.html index d7b8f7353..c721c8a34 100644 --- a/helios/templates/election_questions.html +++ b/helios/templates/election_questions.html @@ -2,7 +2,7 @@ {% block title %}Questions for {{election.name}}{% endblock %} {% block content %} -

{{election.name}} — Questions [back to election]

+

{{election.name}} — Questions [back to election]

-

{{election.name}} — Trustee {{trustee.name}} — Check Secret Key [back to trustee home]

+

{{election.name}} — Trustee {{trustee.name}} — Check Secret Key [back to trustee home]

Your public key fingerprint is: {{trustee.public_key_hash}} @@ -59,7 +59,7 @@

{{election.name}} — Trustee {{trustee.name}} — Che

-
diff --git a/helios/templates/trustee_decrypt_and_prove.html b/helios/templates/trustee_decrypt_and_prove.html index d4cbe4384..cb4c2e68c 100644 --- a/helios/templates/trustee_decrypt_and_prove.html +++ b/helios/templates/trustee_decrypt_and_prove.html @@ -157,7 +157,7 @@

Trustee {{trustee.name}} — Decrypt Result for {{election

FIRST STEP: enter your secret key

- +

@@ -187,17 +187,15 @@

SECOND STEP: upload your partial decryption

When you're ready, you can submit this result to the server.

Your partial decryption:
-
-

- -
-
- reset and restart decryption process -
+

+ + +

+

reset and restart decryption process

diff --git a/helios/templates/trustee_home.html b/helios/templates/trustee_home.html index 5f6ee9c54..8d6dd1151 100644 --- a/helios/templates/trustee_home.html +++ b/helios/templates/trustee_home.html @@ -7,9 +7,9 @@

{{election.name}} — Trustee {{trustee.name}} Home {% if trustee.public_key_hash %} You have successfully uploaded your public key.
Your public key fingerprint is: {{trustee.public_key_hash}}.
-You can verify that you have the right secret key. +You can verify that you have the right secret key. {% else %} -setup your key +setup your key {% endif %}

@@ -19,7 +19,7 @@

{{election.name}} — Trustee {{trustee.name}} Home You have successfully uploaded your decryption. {% else %} The encrypted tally for this election is ready.
- decrypt with your key + decrypt with your key {% endif %} {% else %} Once the tally is computed, come back here to provide your secret key for decryption purposes.
diff --git a/helios/templates/voters_eligibility.html b/helios/templates/voters_eligibility.html index 51343befd..0baa6f7f9 100644 --- a/helios/templates/voters_eligibility.html +++ b/helios/templates/voters_eligibility.html @@ -2,7 +2,7 @@ {% block title %}Voter Eligibility for {{election.name}}{% endblock %} {% block content %} -

{{election.name}} — Voter Eligibility [back to voters]

+

{{election.name}} — Voter Eligibility [back to voters]

{{election.pretty_eligibility|safe}} diff --git a/helios/templates/voters_email.html b/helios/templates/voters_email.html index 535f7533f..62a6c7d63 100644 --- a/helios/templates/voters_email.html +++ b/helios/templates/voters_email.html @@ -9,7 +9,7 @@ {% endif %} -

{{election.name}} — Contact Voters [back to election]

+

{{election.name}} — Contact Voters [back to election]

{% if voter %}

@@ -55,7 +55,7 @@

{{election.name}} — Contact Voters - Done, go back to election. + Done, go back to election.