From e7ec6f161d0cfe7d13364751412d652036b1773d Mon Sep 17 00:00:00 2001
From: Ben Adida <ben@adida.net>
Date: Wed, 8 Dec 2010 18:43:42 -0800
Subject: [PATCH] added private_p field to election, more tests, and continued
 election-specific voter passwords port

---
 helios/fixtures/election.json       | 23 +++++++++++++++++++++
 helios/fixtures/users.json          |  2 +-
 helios/forms.py                     |  1 +
 helios/models.py                    | 17 ++++++++++------
 helios/templates/election_view.html |  2 +-
 helios/tests.py                     | 31 ++++++++++++++++++++++++-----
 helios/views.py                     |  2 +-
 heliosbooth                         |  2 +-
 8 files changed, 65 insertions(+), 15 deletions(-)
 create mode 100644 helios/fixtures/election.json

diff --git a/helios/fixtures/election.json b/helios/fixtures/election.json
new file mode 100644
index 0000000..6159a99
--- /dev/null
+++ b/helios/fixtures/election.json
@@ -0,0 +1,23 @@
+[{"pk": 1000,
+  "model": "helios.election",
+  "fields":
+      {
+            "admin": 1,
+            "uuid" : "206ef039-05c9-4e9c-bb8f-963da50c08d4",
+            "short_name" : "test",
+            "name" : "Test Election",
+            "election_type" : "election",
+            "advanced_audit_features" : true,
+            "private_p" : false,
+            "description" : "test descriptoin",
+            "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 f764ccb..e66e46d 100644
--- a/helios/fixtures/users.json
+++ b/helios/fixtures/users.json
@@ -1 +1 @@
-[{"pk": 1, "model": "auth.user", "fields": {"info": "{u'password': u'test'}", "user_id": "foobar", "name": "Foo Bar", "user_type": "password", "token": null, "admin_p": false}}]
\ No newline at end of file
+[{"pk": 1, "model": "auth.user", "fields": {"info": "{}", "user_id": "ben@adida.net", "name": "Ben Adida", "user_type": "google", "token": null, "admin_p": false}}]
\ No newline at end of file
diff --git a/helios/forms.py b/helios/forms.py
index 83f05d8..3ac8951 100644
--- a/helios/forms.py
+++ b/helios/forms.py
@@ -14,6 +14,7 @@ class ElectionForm(forms.Form):
   election_type = forms.ChoiceField(label="type", choices = Election.ELECTION_TYPES)
   use_voter_aliases = forms.BooleanField(required=False, initial=False, help_text='if selected, voter identities will be replaced with aliases, e.g. "V12", in the ballot tracking center')
   advanced_audit_features = forms.BooleanField(required=False, initial=True, help_text='disable this only if you want a simple election with reduced security but a simpler user interface')
+  private_p = forms.BooleanField(required=False, initial=False, label="Private?", help_text='a private election is only visible to registered/eligible voters')
   
 
 class ElectionTimesForm(forms.Form):
diff --git a/helios/models.py b/helios/models.py
index 673dae2..0de508e 100644
--- a/helios/models.py
+++ b/helios/models.py
@@ -42,6 +42,7 @@ class Election(models.Model, electionalgs.Election):
 
   election_type = models.CharField(max_length=250, null=False, default='election', choices = ELECTION_TYPES)
   advanced_audit_features = models.BooleanField(default=True, null=False)
+  private_p = models.BooleanField(default=False, null=False)
 
   description = models.TextField()
   public_key = JSONField(algs.EGPublicKey, null=True)
@@ -520,6 +521,7 @@ class VoterFile(models.Model):
       if not voter:
         voter_uuid = str(uuid.uuid4())
         voter = Voter(uuid= voter_uuid, user = None, voter_login_id = voter_id, voter_name = name, election = election)
+        voter.generate_password()
         new_voters.append(voter)
         voter.save()
 
@@ -577,7 +579,7 @@ class Voter(models.Model, electionalgs.Voter):
     return voter
 
   @classmethod
-  def get_by_election(cls, election, cast=None, order_by='voter_id', after=None, limit=None):
+  def get_by_election(cls, election, cast=None, order_by='voter_login_id', after=None, limit=None):
     """
     FIXME: review this for non-GAE?
     """
@@ -624,11 +626,9 @@ class Voter(models.Model, electionalgs.Voter):
     
   @classmethod
   def get_by_election_and_user(cls, election, user):
-    query = cls.objects.filter(election = election, user = user)
-
     try:
-      return query[0]
-    except:
+      return cls.objects.get(election = election, user = user)
+    except cls.DoesNotExist:
       return None
       
   @classmethod
@@ -668,7 +668,7 @@ class Voter(models.Model, electionalgs.Voter):
       return self.user.user_type
     else:
       return 'password'
-  
+
   @property
   def display_html_big(self):
     if self.user:
@@ -676,6 +676,11 @@ class Voter(models.Model, electionalgs.Voter):
     else:
       return """<img border="0" height="25" src="/static/auth/login-icons/password.png" alt="password" /> %s""" % self.name
       
+  def generate_password(self, length=10):
+    if self.voter_password:
+      raise Exception("password already exists")
+    
+    self.voter_password = heliosutils.random_string(length)
 
   def store_vote(self, cast_vote):
     # only store the vote if it's cast later than the current one
diff --git a/helios/templates/election_view.html b/helios/templates/election_view.html
index 1a007f4..abf42ed 100644
--- a/helios/templates/election_view.html
+++ b/helios/templates/election_view.html
@@ -24,7 +24,7 @@ if (!navigator.javaEnabled()) {
 {% endif %}
 {% endif %}</h2>
 <p style="padding-top:0px; margin-top:0px">
-{{ election.election_type }} created by <u><b>{{election.admin.display_html_small|safe}}</b></u>
+<em>{% if election.private_p %}private{%else%}public{% endif %}</em> {{ election.election_type }} created by <u><b>{{election.admin.display_html_small|safe}}</b></u>
 {% if election.is_archived %}
 [archived]
 {% endif %}
diff --git a/helios/tests.py b/helios/tests.py
index 3130d88..12b99ce 100644
--- a/helios/tests.py
+++ b/helios/tests.py
@@ -15,6 +15,8 @@ from django.test import TestCase
 
 from django.core import mail
 
+import uuid
+
 class ElectionModelTests(TestCase):
     fixtures = ['users.json']
 
@@ -33,7 +35,7 @@ class ElectionModelTests(TestCase):
         self.election.generate_trustee(ELGAMAL_PARAMS)
     
     def setUp(self):
-        self.user = auth_models.User.objects.get(user_id='foobar')
+        self.user = auth_models.User.objects.get(user_id='ben@adida.net', user_type='google')
         self.election, self.created_p = self.create_election()
 
     def test_create_election(self):
@@ -101,7 +103,7 @@ class ElectionModelTests(TestCase):
         self.assertEquals(LOGS,pulled_logs)
 
     def test_eligibility(self):
-        self.election.eligibility = [{'auth_system': 'password'}]
+        self.election.eligibility = [{'auth_system': self.user.user_type}]
 
         # without openreg, this should be false
         self.assertFalse(self.election.user_eligible_p(self.user))
@@ -151,7 +153,9 @@ class ElectionModelTests(TestCase):
         
         # make sure voter is there now
         voter_2 = models.Voter.get_by_election_and_user(self.election, self.user)
+
         self.assertFalse(voter == None)
+        self.assertFalse(voter_2 == None)
         self.assertEquals(voter, voter_2)
 
         # make sure voter is there in this call too
@@ -159,10 +163,27 @@ class ElectionModelTests(TestCase):
         self.assertTrue(len(voters) == 1)
         self.assertEquals(voter, voters[0])
 
-        voter_2 = models.Voter.get_by_election_and_voter_id(self.election, voter.voter_id)
-        self.assertEquals(voter, voter_2)
-
         voter_2 = models.Voter.get_by_election_and_uuid(self.election, voter.uuid)
         self.assertEquals(voter, voter_2)
 
         self.assertEquals(voter.user, self.user)
+
+
+class VoterModelTests(TestCase):
+    fixtures = ['users.json', 'election.json']
+
+    def setUp(self):
+        self.election = models.Election.objects.get(short_name='test')
+
+    def test_create_password_voter(self):
+        v = models.Voter(uuid = uuid.uuid1(), election = self.election, voter_login_id = 'voter_test_1', voter_name = 'Voter Test 1')
+        v.generate_password()
+
+        v.save()
+        
+        # password has been generated!
+        self.assertFalse(v.voter_password == None)
+
+        # can't generate passwords twice
+        self.assertRaises(Exception, lambda: v.generate_password())
+        
diff --git a/helios/views.py b/helios/views.py
index 1069861..5a2b560 100644
--- a/helios/views.py
+++ b/helios/views.py
@@ -170,7 +170,7 @@ def election_new(request):
 def one_election_edit(request, election):
 
   error = None
-  RELEVANT_FIELDS = ['short_name', 'name', 'description', 'use_voter_aliases', 'election_type', 'advanced_audit_features']
+  RELEVANT_FIELDS = ['short_name', 'name', 'description', 'use_voter_aliases', 'election_type', 'advanced_audit_features', 'private_p']
   
   if request.method == "GET":
     values = {}
diff --git a/heliosbooth b/heliosbooth
index c5ce1b4..8686477 160000
--- a/heliosbooth
+++ b/heliosbooth
@@ -1 +1 @@
-Subproject commit c5ce1b4a5e9cbd93a4e39a738a6ca39db046d5f3
+Subproject commit 868647746ac7033b77afc1ee5e09baddbe57ce20
-- 
GitLab