pysteve/www/wsgi/rest_voter.py (152 lines of code) (raw):

#!/usr/bin/env python ##### # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ##### import os, sys, re, cgi, hashlib, re version = 2 if sys.hexversion < 0x03000000: import ConfigParser as configparser else: import configparser version = 3 path = os.path.abspath(os.getcwd()) sys.path.append(path) sys.path.append(os.path.basename(sys.argv[0])) sys.path.append(os.path.dirname(__file__)) # Fetch config (hack, hack, hack) config = configparser.RawConfigParser() config.read('/var/www/steve/steve.cfg') #FIXME! # Some quick paths homedir = config.get("general", "homedir") from lib import response, voter, election def application (environ, start_response): pathinfo = environ['PATH_INFO'] if 'PATH_INFO' in environ else None whoami = environ['REMOTE_USER'] if 'REMOTE_USER' in environ else None karma = 0 if whoami and config.has_option("karma", whoami): karma = int(config.get("karma", whoami)) form = cgi.FieldStorage( fp=environ['wsgi.input'], environ=environ, keep_blank_values=True ) # Figure out what to do and where if pathinfo: l = pathinfo.split("/") if l[0] == "": l.pop(0) action = l[0] electionID = l[1] if len(l) > 1 else None issueID = l[2] if len(l) > 2 else None voterID = form.getvalue('uid') if not voterID and karma < 3 and (action != "request" and action != "peek"): return response.wsgirespond(start_response, 403, {'message': "Voter UID missing"}) elif electionID and re.search(r"([^A-Za-z0-9-.])", electionID): return response.wsgirespond(start_response, 400, {'message': "Invalid election ID specified"}) elif issueID and re.search(r"([^A-Za-z0-9-.])", issueID): return response.wsgirespond(start_response, 400, {'message': "Invalid issue ID specified"}) elif action == "view": # View a list of issues for an election if electionID and not issueID: js = [] if election.exists(electionID): try: basedata = election.getBasedata(electionID) if not basedata: raise Exception("Could not load base data") if karma < 3 and not voter.get(electionID, basedata, voterID): raise Exception("Invalid voter ID presented") if 'closed' in basedata and basedata['closed'] == True: raise Exception("This election has closed") for issueID in election.listIssues(electionID): try: entry = election.getIssue(electionID, issueID) entry['hasVoted'] = voter.hasVoted(electionID, issueID, voterID) js.append(entry) except Exception as err: return response.wsgirespond(start_response, 500, {'message': 'Could not load issues: %s' % err}) except Exception as err: return response.wsgirespond(start_response, 500, {'message': 'Could not load base data: %s' % err}) if 'hash' in basedata: del basedata['hash'] return response.wsgirespond(start_response, 200, {'base_data': basedata, 'issues': js, 'baseurl': config.get("general", "rooturl")}) else: return response.wsgirespond(start_response, 404, {'message': 'No such election'}) # View a speficic issue elif electionID and issueID: js = [] issuedata = election.getIssue(electionID, issueID) if issuedata: try: basedata = election.getBasedata(electionID) if karma < 3 and not voter.get(electionID, basedata, voterID): raise Exception("Invalid voter ID presented") if 'closed' in basedata and basedata['closed'] == True: raise Exception("This election has closed") entry = election.getIssue(electionID, issueID) return response.wsgirespond(start_response, 200, {'issue': entry}) except Exception as err: return response.wsgirespond(start_response, 500, {'message': "Could not load issue: %s" % err}) else: return response.wsgirespond(start_response, 404, {'message': 'No such issue'}) else: return response.wsgirespond(start_response, 404, {'message': 'No election ID supplied'}) elif action == "vote" and electionID and issueID and voterID: try: basedata = election.getBasedata(electionID) issuedata = election.getIssue(electionID, issueID) if basedata and issuedata: if 'closed' in basedata and basedata['closed'] == True: raise Exception("This election has closed") email = voter.get(electionID, basedata, voterID) if not email: return response.wsgirespond(start_response, 403, {'message': 'Could not save vote: Invalid voter ID presented'}) else: vote = form.getvalue('vote') if not vote: return response.wsgirespond(start_response, 500, {'message': 'Please specify a vote'}) else: invalid = election.invalidate(issuedata, vote) if invalid: return response.wsgirespond(start_response, 500, {'message': invalid}) else: votehash = election.vote(electionID, issueID, voterID, vote) voteuid = hashlib.sha224(voterID).hexdigest() # Catch proxy-emails m = re.match(r"^(.+@.*?[a-zA-Z])-.+$", email) if m: email = m.group(1) voter.email(email, "Vote registered: %s (%s)" % (issueID, issuedata['title']), "This is a receipt that your vote was registered for issue #%s:\n\nElection: %s (%s)\nIssue: %s (%s)\nVote cryptohash: %s\nVote UID: %s" % (issueID, basedata['title'], electionID, issuedata['title'], issueID, votehash, voteuid)) return response.wsgirespond(start_response, 200, {'message': 'Vote saved!'}) else: return response.wsgirespond(start_response, 404, {'message': 'Could not save vote: No such election or issue'}) except Exception as err: return response.wsgirespond(start_response, 500, {'message': 'Could not save vote: %s' % err}) elif action == "request" and election: email = form.getvalue('email') if not email or len(email) > 300 or not re.match(r"([^@]+@[^@]+)", email): return response.wsgirespond(start_response, 400, {'message': 'Could not request voter ID: Invalid email address specified'}) else: try: basedata = election.getBasedata(electionID) if basedata: if 'closed' in basedata and basedata['closed'] == True: raise Exception("This election has closed") if 'open' in basedata and basedata['open'] == "true": uid, xhash = voter.add(electionID, basedata, email) voter.email(email, "Your voter link for %s" % basedata['title'], "Your personal vote link is: %s/election.html?%s/%s\nDo not share this link with anyone." % (config.get("general", "rooturl"), electionID, uid)) return response.wsgirespond(start_response, 200, {'message': "Voter ID sent via email"}) else: return response.wsgirespond(start_response, 403, {'message': "Could not request voter ID: This eleciton is closed for the public"}) else: return response.wsgirespond(start_response, 404, {'message': 'Could not request voter ID: No such election'}) except Exception as err: return response.wsgirespond(start_response, 500, {'message': 'Could not create voter ID: %s' % err}) elif action == "peek" and election: try: basedata = election.getBasedata(electionID, hideHash=True) if basedata: if 'closed' in basedata and basedata['closed'] == True: raise Exception("This election has closed") if 'open' in basedata and basedata['open'] == "true": return response.wsgirespond(start_response, 200, { 'base_data': basedata } ) else: return response.wsgirespond(start_response, 403, {'message': 'This election is not open to the public'}) else: return response.wsgirespond(start_response, 404, {'message': 'Could not request data: No such election'}) except Exception as err: return response.wsgirespond(start_response, 500, {'message': 'Could not load election data: %s' % err}) else: return response.wsgirespond(start_response, 400, {'message': 'Invalid action supplied'}) else: return response.wsgirespond(start_response, 500, {'message': 'No path info supplied, aborting'})