Skip to content

Commit

Permalink
added more true integration tests with dependencey django-webtest and…
Browse files Browse the repository at this point in the history
… webtest
  • Loading branch information
benadida committed Jun 5, 2011
1 parent 5c49182 commit 74021d7
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 56 deletions.
4 changes: 4 additions & 0 deletions README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ NEEDS:
- South for schema migration
-- easy_install South

- django-webtest for testing
-- http://pypi.python.org/pypi/django-webtest
-- easy_install webtest
-- easy_install django-webtest

GETTING SOUTH WORKING ON EXISTING INSTALL
- as of Helios v3.0.4, we're using South to migrate data models
Expand Down
37 changes: 34 additions & 3 deletions helios/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@

import helios

# current voter
def get_voter(request, user, election):
"""
return the current voter
"""
voter = None
if request.session.has_key('CURRENT_VOTER'):
voter = request.session['CURRENT_VOTER']
if voter.election != election:
voter = None

if not voter:
if user:
voter = Voter.get_by_election_and_user(election, user)

return voter

# a function to check if the current user is a trustee
HELIOS_TRUSTEE_UUID = 'helios_trustee_uuid'
def get_logged_in_trustee(request):
Expand Down Expand Up @@ -80,9 +97,8 @@ 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 get_voter, get_user, password_voter_login
user = get_user(request)
if not user_can_admin_election(user, election) and not get_voter(request, user, election):
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_url' : return_url
Expand All @@ -101,6 +117,21 @@ def user_can_admin_election(user, election):
# election or site administrator
return election.admin == user or user.admin_p

def user_can_see_election(request, election):
user = get_user(request)

if not election.private_p:
return True

# election is private

# but maybe this user is the administrator?
if user_can_admin_election(user, election):
return True

# then this user has to be a voter
return (get_voter(request, user, election) != None)

def api_client_can_admin_election(api_client, election):
return election.api_client == api_client and api_client != None

Expand Down
113 changes: 77 additions & 36 deletions helios/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import unittest, datetime, re
import django_webtest

import models
import datatypes
Expand Down Expand Up @@ -316,11 +317,30 @@ class LegacyElectionBlackboxTests(DataFormatBlackboxTests, TestCase):
# EXPECTED_TRUSTEES_FILE = 'helios/fixtures/v3.1-trustees-expected.json'
# EXPECTED_BALLOTS_FILE = 'helios/fixtures/v3.1-ballots-expected.json'

class WebTest(django_webtest.WebTest):
def assertRedirects(self, response, url):
"""
reimplement this in case it's a WebOp response
"""
if hasattr(response, 'status_code'):
return super(django_webtest.WebTest, self).assertRedirects(response, url)

assert response.status_int == 302
assert url in response.location, "redirected to %s instead of %s" % (response.location, url)

def assertContains(self, response, text):
if hasattr(response, 'status_code'):
return super(django_webtest.WebTest, self).assertContains(response, text)

assert response.status_int == 200
assert text in response.testbody, "missing text %s" % text


##
## overall operation of the system
##

class ElectionBlackboxTests(TestCase):
class ElectionBlackboxTests(WebTest):
fixtures = ['users.json', 'election.json']

def setUp(self):
Expand All @@ -331,7 +351,10 @@ def setup_login(self):
# set up the session
session = self.client.session
session['user'] = {'type': self.user.user_type, 'user_id': self.user.user_id}
session.save()
session.save()

# set up the app, too
self.app.cookies['sessionid'] = self.client.cookies.get('sessionid').value

def clear_login(self):
session = self.client.session
Expand Down Expand Up @@ -513,38 +536,45 @@ def _cast_ballot(self, election_id, username, password, need_login=True, check_u
check_user_logged_in looks for the "you're already logged" message
"""
# vote by preparing a ballot via the server-side encryption
response = self.client.post("/helios/elections/%s/encrypt-ballot" % election_id, {
response = self.app.post("/helios/elections/%s/encrypt-ballot" % election_id, {
'answers_json': utils.to_json([[1]])})
self.assertContains(response, "answers")

# parse it as an encrypted vote, and re-serialize it
ballot = datatypes.LDObject.fromDict(utils.from_json(response.content), type_hint='legacy/EncryptedVote')
ballot = datatypes.LDObject.fromDict(utils.from_json(response.testbody), type_hint='legacy/EncryptedVote')
encrypted_vote = ballot.serialize()

# cast the ballot
response = self.client.post("/helios/elections/%s/cast" % election_id, {
response = self.app.post("/helios/elections/%s/cast" % election_id, {
'encrypted_vote': encrypted_vote})
self.assertRedirects(response, "%s/helios/elections/%s/cast_confirm" % (settings.SECURE_URL_HOST, election_id))

cast_confirm_page = response.follow()

if need_login:
if check_user_logged_in:
response = self.client.get("/helios/elections/%s/cast_confirm" % election_id)
self.assertContains(response, "You are logged in as")
self.assertContains(response, "requires election-specific credentials")

response = self.client.post("/helios/elections/%s/password_voter_login" % election_id, {
'voter_id' : username,
'password' : password
})
self.assertRedirects(response, "/helios/elections/%s/cast_confirm" % election_id)
else:
response = self.client.get("/helios/elections/%s/cast_confirm" % election_id)
self.assertContains(response, "I am ")
self.assertContains(cast_confirm_page, "You are logged in as")
self.assertContains(cast_confirm_page, "requires election-specific credentials")

# confirm the vote
response = self.client.post("/helios/elections/%s/cast_confirm" % election_id, {
"csrf_token" : self.client.session['csrf_token'],
"status_update" : False})
# set the form
login_form = cast_confirm_page.form
login_form['voter_id'] = username
login_form['password'] = password

cast_confirm_page = login_form.submit()

self.assertRedirects(cast_confirm_page, "/helios/elections/%s/cast_confirm" % election_id)
cast_confirm_page = cast_confirm_page.follow()

# here we should be at the cast-confirm page and logged in
self.assertContains(cast_confirm_page, "I am ")

# confirm the vote, now with the actual form
cast_form = cast_confirm_page.form

if 'status_update' in cast_form.fields.keys():
cast_form['status_update'] = False
response = cast_form.submit()
self.assertRedirects(response, "%s/helios/elections/%s/cast_done" % (settings.URL_HOST, election_id))

# at this point an email should have gone out to the user
Expand All @@ -559,18 +589,25 @@ def _cast_ballot(self, election_id, username, password, need_login=True, check_u
# so if need_login is False, it was a private election, and we do need to re-login here
# we need to re-login if it's a private election, because all data, including ballots
# is otherwise private
response = self.client.post("/helios/elections/%s/password_voter_login" % election_id, {
'voter_id' : username,
'password' : password
})
login_page = self.app.get("/helios/elections/%s/password_voter_login" % election_id)

# if we redirected, that's because we can see the page, I think
if login_page.status_int != 302:
login_form = login_page.form

login_form['voter_id'] = username
login_form['password'] = password
login_form.submit()

response = self.client.get(url)
response = self.app.get(url)
self.assertContains(response, ballot.hash)
self.assertContains(response, html_escape(encrypted_vote))

# if we request the redirect to cast_done, the voter should be logged out, but not the user
response = self.client.get("/helios/elections/%s/cast_done" % election_id)
assert not self.client.session.has_key('CURRENT_VOTER')
response = self.app.get("/helios/elections/%s/cast_done" % election_id)

# FIXME: how to check this? We can't do it by checking session that we're doign webtes
# assert not self.client.session.has_key('CURRENT_VOTER')

def _do_tally(self, election_id):
# log back in as administrator
Expand Down Expand Up @@ -615,15 +652,19 @@ def test_do_complete_election_private(self):
# private election
election_id, username, password = self._setup_complete_election({'private_p' : "1"})

# log in
response = self.client.post("/helios/elections/%s/password_voter_login" % election_id, {
'voter_id' : username,
'password' : password,
'return_url' : "/helios/elections/%s/view" % election_id
})
# get the password_voter_login_form via the front page
# (which will test that redirects are doing the right thing)
response = self.app.get("/helios/elections/%s/view" % election_id)

# ensure it redirects
self.assertRedirects(response, "/helios/elections/%s/password_voter_login" % election_id)

login_form = response.follow().form

login_form['voter_id'] = username
login_form['password'] = password

# FIXME: probably better to fetch password_voter_login as a get and post the form obtained
# rather than assume return_url
response = login_form.submit()
self.assertRedirects(response, "/helios/elections/%s/view" % election_id)

self._cast_ballot(election_id, username, password, need_login = False)
Expand Down
24 changes: 7 additions & 17 deletions helios/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,22 +100,6 @@ def stats(request):
'limit' : limit})


def get_voter(request, user, election):
"""
return the current voter
"""
voter = None
if request.session.has_key('CURRENT_VOTER'):
voter = request.session['CURRENT_VOTER']
if voter.election != election:
voter = None

if not voter:
if user:
voter = Voter.get_by_election_and_user(election, user)

return voter

##
## simple admin for development
##
Expand Down Expand Up @@ -546,10 +530,15 @@ def password_voter_login(request, election):
"""
This is used to log in as a voter for a particular election
"""

# the URL to send the user to after they've logged in
return_url = request.REQUEST.get('return_url', reverse(one_election_cast_confirm, args=[election.uuid]))
if request.method == "GET":
# if user logged in somehow in the interim, e.g. using the login link for administration,
# then go!
if user_can_see_election(request, election):
return HttpResponseRedirect(reverse(one_election_view, args = [election.uuid]))

password_login_form = forms.VoterPasswordForm()
return render_template(request, 'password_voter_login', {'election': election,
'return_url' : return_url,
Expand Down Expand Up @@ -663,6 +652,7 @@ def one_election_cast_confirm(request, election):
return render_template(request, 'election_cast_confirm', {
'login_box': login_box, 'election' : election, 'vote_fingerprint': vote_fingerprint,
'past_votes': past_votes, 'issues': issues, 'voter' : voter,
'return_url': return_url,
'status_update_label': status_update_label, 'status_update_message': status_update_message,
'show_password': show_password, 'password_only': password_only, 'password_login_form': password_login_form,
'bad_voter_login': bad_voter_login})
Expand Down

0 comments on commit 74021d7

Please sign in to comment.