diff --git a/.travis.yml b/.travis.yml index 1505fccb9e5bd689f6964ac911b32fa86dac2b92..5d8f2f0fef02feacca7bf20e861675293df3dd2f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ sudo: false language: python python: - - "2.7" + - "3.7" os: linux @@ -9,13 +9,13 @@ before_install: - export BOTO_CONFIG=/dev/null install: - - pip install --upgrade pip - - pip install -r requirements.txt + - pip3 install --upgrade pip + - pip3 install -r requirements.txt before_script: - psql -c 'create database helios;' -U postgres -script: "python -Wall manage.py test" +script: "python3 -Wall manage.py test" jobs: include: diff --git a/helios/__init__.py b/helios/__init__.py index 2f598298b565f3a883cf9365699a6911031e0120..21f21bc59da930240e0c88612f1afcc9c0a46178 100644 --- a/helios/__init__.py +++ b/helios/__init__.py @@ -1,7 +1,7 @@ from django.conf import settings # 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 +from .celery_app import app as celery_app __all__ = ('celery_app', 'TEMPLATE_BASE', 'ADMIN_ONLY', 'VOTERS_UPLOAD', 'VOTERS_EMAIL',) diff --git a/helios/crypto/algs.py b/helios/crypto/algs.py index 8ef36212bef0e8dfd87963585e5d3d7845637415..b7439d9bd3d50cd738b354fcfbfe64495df0db78 100644 --- a/helios/crypto/algs.py +++ b/helios/crypto/algs.py @@ -6,14 +6,14 @@ FIXME: improve random number generation. Ben Adida ben@adida.net """ -from __future__ import print_function -import hashlib import logging +from Crypto.Hash import SHA1 from Crypto.Util import number from helios.crypto.utils import random +from helios.utils import to_json class ElGamal: @@ -117,8 +117,7 @@ class EGPublicKey: # 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: @@ -263,7 +262,7 @@ class EGSecretKey: 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 @@ -299,7 +298,7 @@ class EGSecretKey: sk = cls() sk.x = int(d['x']) - if d.has_key('public_key'): + if 'public_key' in d: sk.pk = EGPublicKey.from_dict(d['public_key']) else: sk.pk = None @@ -333,7 +332,7 @@ class EGCiphertext: """ 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: @@ -699,7 +698,7 @@ def EG_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 @@ -709,4 +708,4 @@ def EG_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/electionalgs.py b/helios/crypto/electionalgs.py index b9a52753e75fa78ddbda47dfa53c83540bbc1fe1..1f7593b14031ffb397352dcdf52a721e984f52d6 100644 --- a/helios/crypto/electionalgs.py +++ b/helios/crypto/electionalgs.py @@ -7,8 +7,9 @@ Ben Adida import datetime import uuid -import algs -import utils +from helios.utils import to_json +from . import algs +from . import utils class HeliosObject(object): @@ -29,7 +30,7 @@ class HeliosObject(object): def set_from_args(self, **kwargs): for f in self.FIELDS: - if kwargs.has_key(f): + if f in kwargs: new_val = self.process_value_in(f, kwargs[f]) setattr(self, f, new_val) else: @@ -43,7 +44,7 @@ class HeliosObject(object): setattr(self, f, None) def toJSON(self): - return utils.to_json(self.toJSONDict()) + return to_json(self.toJSONDict()) def toJSONDict(self, alternate_fields=None): val = {} @@ -55,7 +56,7 @@ class HeliosObject(object): def fromJSONDict(cls, d): # go through the keys and fix them new_d = {} - for k in d.keys(): + for k in list(d.keys()): new_d[str(k)] = d[k] return cls(**new_d) @@ -78,7 +79,7 @@ class HeliosObject(object): @property def hash(self): - s = utils.to_json(self.toJSONDict()) + s = to_json(self.toJSONDict()) return utils.hash_b64(s) def process_value_in(self, field_name, field_value): @@ -230,7 +231,7 @@ class EncryptedAnswer(HeliosObject): else: ea.overall_proof = None - if d.has_key('randomness'): + if 'randomness' in d: ea.randomness = [int(r) for r in d['randomness']] ea.answer = d['answer'] @@ -264,7 +265,7 @@ class EncryptedAnswer(HeliosObject): # min and max for number of answers, useful later min_answers = 0 - if question.has_key('min'): + if 'min' in question: min_answers = question['min'] max_answers = question['max'] @@ -340,7 +341,7 @@ class EncryptedVote(HeliosObject): question = election.questions[question_num] min_answers = 0 - if question.has_key('min'): + if 'min' in question: min_answers = question['min'] if not ea.verify(election.public_key, min=min_answers, max=question['max']): @@ -349,7 +350,7 @@ class EncryptedVote(HeliosObject): return True def get_hash(self): - return utils.hash_b64(utils.to_json(self.toJSONDict())) + return utils.hash_b64(to_json(self.toJSONDict())) def toJSONDict(self, with_randomness=False): return { @@ -383,7 +384,7 @@ 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 = sorted(enumerate(result), key=lambda x: x[1]) counts.reverse() # if there's a max > 1, we assume that the top MAX win @@ -416,7 +417,7 @@ class Election(HeliosObject): 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: + if isinstance(field_value, str): return datetime.datetime.strptime(field_value, '%Y-%m-%d %H:%M:%S') if field_name == 'public_key': @@ -553,7 +554,7 @@ class CastVote(HeliosObject): def _process_value_in(self, field_name, field_value): if field_name == 'cast_at': - if type(field_value) == str: + if isinstance(field_value, str): return datetime.datetime.strptime(field_value, '%Y-%m-%d %H:%M:%S') if field_name == 'vote': diff --git a/helios/crypto/elgamal.py b/helios/crypto/elgamal.py index c6b89b3820284e5c9df17849be4c5fe77e02d917..33eb03083319aa0b7f9ab61defbca3a50bd57440 100644 --- a/helios/crypto/elgamal.py +++ b/helios/crypto/elgamal.py @@ -192,7 +192,7 @@ class SecretKey: a = pow(self.pk.g, w, self.pk.p) b = pow(ciphertext.alpha, w, self.pk.p) - c = int(SHA1.new(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 +232,7 @@ class Ciphertext: """ 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: @@ -278,10 +278,10 @@ class Ciphertext: """ 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): """ @@ -393,7 +393,7 @@ class Ciphertext: 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") @@ -503,7 +503,7 @@ def disjunctive_challenge_generator(commitments): array_to_hash.append(str(commitment['B'])) string_to_hash = ",".join(array_to_hash) - return int(SHA1.new(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): @@ -511,5 +511,5 @@ def fiatshamir_challenge_generator(commitment): def DLog_challenge_generator(commitment): string_to_hash = str(commitment) - return int(SHA1.new(string_to_hash).hexdigest(),16) + return int(SHA1.new(bytes(string_to_hash, 'utf-8')).hexdigest(),16) diff --git a/helios/crypto/numtheory.py b/helios/crypto/numtheory.py index 16fcf9aa0c2fe0017471546d0f353c9cb60878bd..e16691745cf7f9dd2713274b67c1a1a0091df12e 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 @@ class Poly: # (1) 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 @@ class Poly: # (1) 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 @@ class Poly: # (1) 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/utils.py b/helios/crypto/utils.py index 08df21b2ced75c8305dd223476c2a8bbb5c1f0eb..d854c886bd6e6a0128fbdeec232305ee97870a2a 100644 --- a/helios/crypto/utils.py +++ b/helios/crypto/utils.py @@ -2,7 +2,6 @@ Crypto Utils """ import base64 -import json import math from Crypto.Hash import SHA256 @@ -30,12 +29,3 @@ def hash_b64(s): hasher = SHA256.new(s.encode('utf-8')) 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) diff --git a/helios/datatypes/__init__.py b/helios/datatypes/__init__.py index 93bca2442f0dbe792bf879afd3e3c704c617a28d..574f8eb9355bcec57a4a60e40f9f8c2154a2af25 100644 --- a/helios/datatypes/__init__.py +++ b/helios/datatypes/__init__.py @@ -25,6 +25,7 @@ And when data comes in: # 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 @@ -36,21 +37,21 @@ def recursiveToDict(obj): 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" % datatype) @@ -58,7 +59,7 @@ def get_class(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 @@ class LDObject(object): @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 @@ -149,9 +150,11 @@ class LDObject(object): 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 @@ class LDObject(object): """ # 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 @@ -195,7 +198,7 @@ class LDObject(object): 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)) @@ -274,8 +277,10 @@ class LDObject(object): 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 diff --git a/helios/datatypes/djangofield.py b/helios/datatypes/djangofield.py index 05f8cf355845ddf0eed23d4efc93526a4a06e269..a299ace6c6a7b8f55e231a5e645d238aceb7f51c 100644 --- a/helios/datatypes/djangofield.py +++ b/helios/datatypes/djangofield.py @@ -6,9 +6,9 @@ http://www.djangosnippets.org/snippets/377/ and adapted to LDObject """ -import json from django.db import models +from helios import utils from . import LDObject @@ -28,36 +28,26 @@ class LDObjectField(models.TextField): """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 return self.from_db_value(value) # noinspection PyUnusedLocal def from_db_value(self, value, *args, **kwargs): - if value is None: - return None - # 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 is not 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 is None: diff --git a/helios/datetimewidget.py b/helios/datetimewidget.py index 81d81e0172f7538df77a8d8be8f95774c8bd4e99..dfd7ec04a42118460a216988fa2a9f5a323c1093 100644 --- a/helios/datetimewidget.py +++ b/helios/datetimewidget.py @@ -14,7 +14,7 @@ import datetime, time from django.utils.safestring import mark_safe # DATETIMEWIDGET -calbtn = u'''<img src="%smedia/admin/img/admin/icon_calendar.gif" alt="calendar" id="%s_btn" style="cursor: pointer;" title="Select date" /> +calbtn = '''<img src="%smedia/admin/img/admin/icon_calendar.gif" alt="calendar" id="%s_btn" style="cursor: pointer;" title="Select date" /> <script type="text/javascript"> Calendar.setup({ inputField : "%s", @@ -51,13 +51,13 @@ class DateTimeWidget(forms.widgets.TextInput): 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'<input%s />%s%s' % (forms.util.flatatt(final_attrs), self.media, cal) + a = '<input%s />%s%s' % (forms.util.flatatt(final_attrs), self.media, cal) return mark_safe(a) def value_from_datadict(self, data, files, name): @@ -84,12 +84,12 @@ class DateTimeWidget(forms.widgets.TextInput): 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/fields.py b/helios/fields.py index cf2ad6c3e7c2c6b506c0080b432f6207f4e37807..8d8e885fec3c13f72a0c24cd6ea0d7a39ab80d5c 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/forms.py b/helios/forms.py index 86fc18e5e3a0e88e4a65f32b62ef12b2bc1dea56..d7919675863641cd24abd5ba3082b0e6014d4e84 100644 --- a/helios/forms.py +++ b/helios/forms.py @@ -3,11 +3,12 @@ Forms for Helios """ from django import forms -from models import Election -from widgets import SplitSelectDateTimeWidget -from fields import SplitDateTimeField 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/load_voter_files.py b/helios/management/commands/load_voter_files.py index c34e682aba306f9bd1380a5ed1dafa91cb247b8b..1e3a79beeecf537b24963a5f2b80f1b46f653dfe 100644 --- a/helios/management/commands/load_voter_files.py +++ b/helios/management/commands/load_voter_files.py @@ -28,7 +28,7 @@ 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): diff --git a/helios/migrations/0001_initial.py b/helios/migrations/0001_initial.py index 8ba6efbb125a7b1c4e1e3ed6e74b831bedaf25f1..886b370219ab7fd2fc6fb218148761ae5c4d026c 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): diff --git a/helios/migrations/0002_castvote_cast_ip.py b/helios/migrations/0002_castvote_cast_ip.py index 47db5b169eccee318b2ccb4fc18f125e984a9c83..bb7a422639f66cd81974549a95c000204ebe13da 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 162d6bb54dcb6c1e74c99e859615d7fe51dc179d..8e6f65266e5c6f04bf3cb07b8927ac94ea2ece95 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 d49bb63753f4438ab79c0ce4101f7e56f7bcf8be..a437c5f11f03a5a0766ecd970a031bda409fe3fd 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/models.py b/helios/models.py index 6cb505524e812dfc2da8b3ff0b73d04d6a834dfa..29596f5077f244f91df6a97e1248a095cfe2feb8 100644 --- a/helios/models.py +++ b/helios/models.py @@ -6,26 +6,26 @@ Ben Adida (ben@adida.net) """ -import datetime - -import bleach import copy import csv +import datetime import io -import unicodecsv import uuid + +import bleach +import unicodecsv from django.conf import settings from django.db import models, transaction -from crypto import algs, utils from helios import datatypes -from helios import utils as heliosutils -from helios.crypto.elgamal import Cryptosystem -from helios.crypto.utils import random +from helios import utils from helios.datatypes.djangofield import LDObjectField # useful stuff in helios_auth from helios_auth.jsonfield import JSONField from helios_auth.models import User, AUTH_SYSTEMS +from .crypto import algs +from .crypto.elgamal import Cryptosystem +from .crypto.utils import random, hash_b64 class HeliosModel(models.Model, datatypes.LDObjectContainer): @@ -182,18 +182,18 @@ class Election(HeliosModel): 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(substring(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): @@ -210,9 +210,9 @@ class Election(HeliosModel): @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: @@ -223,9 +223,9 @@ class Election(HeliosModel): @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: @@ -286,7 +286,7 @@ class Election(HeliosModel): 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? @@ -301,7 +301,7 @@ class Election(HeliosModel): 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: @@ -327,7 +327,7 @@ class Election(HeliosModel): return_val = "<ul>" for constraint in self.eligibility: - if constraint.has_key('constraint'): + if 'constraint' in constraint: for one_constraint in constraint['constraint']: return_val += "<li>%s</li>" % AUTH_SYSTEMS[constraint['auth_system']].pretty_eligibility(one_constraint) else: @@ -341,7 +341,7 @@ class Election(HeliosModel): """ 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): """ @@ -349,12 +349,12 @@ class Election(HeliosModel): 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"} @@ -368,7 +368,7 @@ class Election(HeliosModel): }) 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 @@ -397,8 +397,8 @@ class Election(HeliosModel): 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? @@ -446,7 +446,7 @@ class Election(HeliosModel): 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 @@ -470,7 +470,7 @@ class Election(HeliosModel): """ # 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 @@ -478,7 +478,8 @@ class Election(HeliosModel): # 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] + 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: @@ -555,7 +556,7 @@ class Election(HeliosModel): 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 @@ -599,7 +600,7 @@ class Election(HeliosModel): 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 @@ -682,9 +683,9 @@ def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **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] + yield [str(cell, 'utf-8') for cell in row] except: - yield [unicode(cell, 'latin-1') for cell in row] + yield [str(cell, 'latin-1') for cell in row] def utf_8_encoder(unicode_csv_data): for line in unicode_csv_data: @@ -715,20 +716,25 @@ class VoterFile(models.Model): 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.encode(encoding='utf-8') + elif isinstance(self.voter_file_content, bytes): content = self.voter_file_content + 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') + content = content.replace(b'\r',b'\n').replace(b'\n\n',b'\n') + close = False voter_stream = io.BytesIO(content) else: - voter_stream = open(self.voter_file.path, "rU") + close = True + voter_stream = open(self.voter_file.path, "r") #reader = unicode_csv_reader(voter_stream) reader = unicodecsv.reader(voter_stream, encoding='utf-8') @@ -752,6 +758,8 @@ class VoterFile(models.Model): return_dict['name'] = return_dict['email'] yield return_dict + if close: + voter_stream.close() def process(self): self.processing_started_at = datetime.datetime.utcnow() @@ -778,7 +786,7 @@ class VoterFile(models.Model): existing_voter.save() if election.use_voter_aliases: - voter_alias_integers = range(last_alias_num+1, last_alias_num+1+num_voters) + voter_alias_integers = list(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] @@ -816,8 +824,7 @@ class Voter(HeliosModel): 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) @@ -840,7 +847,7 @@ class Voter(HeliosModel): # 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 @@ -856,14 +863,14 @@ class Voter(HeliosModel): # 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 @@ -947,12 +954,12 @@ class Voter(HeliosModel): 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): @@ -972,7 +979,7 @@ class Voter(HeliosModel): 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 @@ -1166,7 +1173,7 @@ class Trustee(HeliosModel): """ # 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) diff --git a/helios/security.py b/helios/security.py index 88dc6754d0bea4e046b6a3e2bb3f8e7c13bb63b5..2f0852d95eb92485c155f359463162b665c1e00a 100644 --- a/helios/security.py +++ b/helios/security.py @@ -4,21 +4,19 @@ Helios Security -- mostly access control Ben Adida (ben@adida.net) """ +import urllib.parse # nicely update the wrapper function from functools import update_wrapper -from django.urls import reverse +from django.conf import settings from django.core.exceptions import PermissionDenied from django.http import Http404 -from django.conf import settings - -from models import Voter, Trustee, Election -from helios_auth.security import get_user - from django.http import HttpResponseRedirect -import urllib +from django.urls import reverse import helios +from helios_auth.security import get_user +from .models import Voter, Trustee, Election class HSTSMiddleware: @@ -45,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 @@ -59,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 @@ -72,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() @@ -120,10 +118,10 @@ def election_view(**checks): # 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 }))) @@ -158,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/stats_views.py b/helios/stats_views.py index 5e71a32e8dca7cf20a191aeffe9cc8dc5bda3492..e506637fa7b8ba224bf96ce6a544758dbdaacd70 100644 --- a/helios/stats_views.py +++ b/helios/stats_views.py @@ -12,8 +12,8 @@ from django.http import HttpResponseRedirect 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 .security import PermissionDenied +from .view_utils import render_template def require_admin(request): diff --git a/helios/tasks.py b/helios/tasks.py index 8610097182707b2aa40abc68e79c148fa664b19d..a1079c1d33c321747d05d5d8b317015f7c928ba1 100644 --- a/helios/tasks.py +++ b/helios/tasks.py @@ -8,9 +8,9 @@ import copy from celery import shared_task from celery.utils.log import get_logger -import signals -from models import CastVote, Election, Voter, VoterFile -from view_utils import render_template_raw +from . import signals +from .models import CastVote, Election, Voter, VoterFile +from .view_utils import render_template_raw @shared_task diff --git a/helios/tests.py b/helios/tests.py index 157ce2823da9cc332790cea5561cd11d8704730d..7f0c72068dcfba86ee026fb4af5cbd374f66d28a 100644 --- a/helios/tests.py +++ b/helios/tests.py @@ -4,10 +4,10 @@ Unit Tests for Helios import datetime import re -import urllib +import uuid +from urllib.parse import urlencode import django_webtest -import uuid from django.conf import settings from django.core import mail from django.core.files import File @@ -53,45 +53,46 @@ class ElectionModelTests(TestCase): self.assertTrue(self.created_p) # should have a creation time - self.assertNotEquals(self.election.created_at, None) + self.assertNotEqual(self.election.created_at, None) self.assertTrue(self.election.created_at < datetime.datetime.utcnow()) def test_find_election(self): election = models.Election.get_by_user_as_admin(self.user)[0] - self.assertEquals(self.election, election) + self.assertEqual(self.election, election) election = models.Election.get_by_uuid(self.election.uuid) - self.assertEquals(self.election, election) + self.assertEqual(self.election, election) election = models.Election.get_by_short_name(self.election.short_name) - self.assertEquals(self.election, election) + self.assertEqual(self.election, election) def test_setup_trustee(self): self.setup_trustee() - self.assertEquals(self.election.num_trustees, 1) + self.assertEqual(self.election.num_trustees, 1) def test_add_voters_file(self): election = self.election FILE = "helios/fixtures/voter-file.csv" - vf = models.VoterFile.objects.create(election = election, voter_file = File(open(FILE), "voter_file.css")) - vf.process() + with open(FILE, 'r', encoding='utf-8') as f: + vf = models.VoterFile.objects.create(election = election, voter_file = File(f, "voter_file.css")) + vf.process() # make sure that we stripped things correctly voter = election.voter_set.get(voter_login_id = 'benadida5') - self.assertEquals(voter.voter_email, 'ben5@adida.net') - self.assertEquals(voter.voter_name, 'Ben5 Adida') + self.assertEqual(voter.voter_email, 'ben5@adida.net') + self.assertEqual(voter.voter_name, 'Ben5 Adida') def test_check_issues_before_freeze(self): # should be three issues: no trustees, and no questions, and no voters issues = self.election.issues_before_freeze - self.assertEquals(len(issues), 3) + self.assertEqual(len(issues), 3) self.setup_questions() # should be two issues: no trustees, and no voters issues = self.election.issues_before_freeze - self.assertEquals(len(issues), 2) + self.assertEqual(len(issues), 2) self.election.questions = None @@ -99,7 +100,7 @@ class ElectionModelTests(TestCase): # should be two issues: no questions, and no voters issues = self.election.issues_before_freeze - self.assertEquals(len(issues), 2) + self.assertEqual(len(issues), 2) self.setup_questions() @@ -107,7 +108,7 @@ class ElectionModelTests(TestCase): self.setup_openreg() issues = self.election.issues_before_freeze - self.assertEquals(len(issues), 0) + self.assertEqual(len(issues), 0) def test_helios_trustee(self): self.election.generate_trustee(views.ELGAMAL_PARAMS) @@ -115,7 +116,7 @@ class ElectionModelTests(TestCase): self.assertTrue(self.election.has_helios_trustee()) trustee = self.election.get_helios_trustee() - self.assertNotEquals(trustee, None) + self.assertNotEqual(trustee, None) def test_log(self): LOGS = ["testing 1", "testing 2", "testing 3"] @@ -126,7 +127,7 @@ class ElectionModelTests(TestCase): pulled_logs = [l.log for l in self.election.get_log().all()] pulled_logs.reverse() - self.assertEquals(LOGS,pulled_logs) + self.assertEqual(LOGS,pulled_logs) def test_eligibility(self): self.election.eligibility = [{'auth_system': self.user.user_type}] @@ -137,7 +138,7 @@ class ElectionModelTests(TestCase): # what about after saving? self.election.save() e = models.Election.objects.get(uuid = self.election.uuid) - self.assertEquals(e.eligibility, [{'auth_system': self.user.user_type}]) + self.assertEqual(e.eligibility, [{'auth_system': self.user.user_type}]) self.election.openreg = True @@ -166,7 +167,7 @@ class ElectionModelTests(TestCase): self.assertTrue(self.election.user_eligible_p(self.fb_user)) # also check that eligibility_category_id does the right thing - self.assertEquals(self.election.eligibility_category_id('facebook'), '123') + self.assertEqual(self.election.eligibility_category_id('facebook'), '123') def test_freeze(self): # freezing without trustees and questions, no good @@ -194,7 +195,7 @@ class ElectionModelTests(TestCase): def test_voter_registration(self): # before adding a voter voters = models.Voter.get_by_election(self.election) - self.assertEquals(0, len(voters)) + self.assertEqual(0, len(voters)) # make sure no voter yet voter = models.Voter.get_by_election_and_user(self.election, self.user) @@ -202,7 +203,7 @@ class ElectionModelTests(TestCase): # make sure no voter at all across all elections voters = models.Voter.get_by_user(self.user) - self.assertEquals(0, len(voters)) + self.assertEqual(0, len(voters)) # register the voter voter = models.Voter.register_user_in_election(self.user, self.election) @@ -212,17 +213,17 @@ class ElectionModelTests(TestCase): self.assertIsNotNone(voter) self.assertIsNotNone(voter_2) - self.assertEquals(voter, voter_2) + self.assertEqual(voter, voter_2) # make sure voter is there in this call too voters = models.Voter.get_by_user(self.user) - self.assertEquals(1, len(voters)) - self.assertEquals(voter, voters[0]) + self.assertEqual(1, len(voters)) + self.assertEqual(voter, voters[0]) voter_2 = models.Voter.get_by_election_and_uuid(self.election, voter.uuid) - self.assertEquals(voter, voter_2) + self.assertEqual(voter, voter_2) - self.assertEquals(voter.user, self.user) + self.assertEqual(voter.user, self.user) @@ -241,13 +242,13 @@ class VoterModelTests(TestCase): v.save() # password has been generated! - self.assertFalse(v.voter_password == None) + self.assertFalse(v.voter_password is None) # can't generate passwords twice self.assertRaises(Exception, lambda: v.generate_password()) # check that you can get at the voter user structure - self.assertEquals(v.get_user().user_id, v.voter_email) + self.assertEqual(v.get_user().user_id, v.voter_email) class CastVoteModelTests(TestCase): @@ -289,7 +290,7 @@ class DatatypeTests(TestCase): 'B' : '234324243'} ld_obj = datatypes.LDObject.fromDict(original_dict, type_hint = 'legacy/EGZKProofCommitment') - self.assertEquals(original_dict, ld_obj.toDict()) + self.assertEqual(original_dict, ld_obj.toDict()) @@ -303,9 +304,8 @@ class DataFormatBlackboxTests(object): self.election = models.Election.objects.all()[0] def assertEqualsToFile(self, response, file_path): - expected = open(file_path) - self.assertEquals(response.content, expected.read()) - expected.close() + with open(file_path) as expected: + self.assertEqual(response.content, expected.read().encode('utf-8')) def test_election(self): response = self.client.get("/helios/elections/%s" % self.election.uuid, follow=False) @@ -349,10 +349,8 @@ class LegacyElectionBlackboxTests(DataFormatBlackboxTests, TestCase): class WebTest(django_webtest.WebTest): def assertStatusCode(self, response, status_code): - if hasattr(response, 'status_code'): - assert response.status_code == status_code, response.status_code - else: - assert response.status_int == status_code, response.status_int + actual_code = response.status_code if hasattr(response, 'status_code') else response.status_int + assert actual_code == status_code, "%s instad of %s" % (actual_code, status_code) def assertRedirects(self, response, url): @@ -374,11 +372,13 @@ class WebTest(django_webtest.WebTest): self.assertStatusCode(response, 200) if hasattr(response, "testbody"): - assert text in response.testbody, "missing text %s" % text + t = response.testbody elif hasattr(response, "body"): - assert text in response.body, "missing text %s" % text + t = response.body else: - assert text in response.content, "missing text %s" % text + t = response.content + + assert text in str(t), "missing text %s" % text ## @@ -418,7 +418,7 @@ class ElectionBlackboxTests(WebTest): def test_election_params(self): response = self.client.get("/helios/elections/params") - self.assertEquals(response.content, views.ELGAMAL_PARAMS_LD_OBJECT.serialize()) + self.assertEqual(response.content, views.ELGAMAL_PARAMS_LD_OBJECT.serialize().encode('utf-8')) def test_election_404(self): response = self.client.get("/helios/elections/foobar") @@ -434,7 +434,7 @@ class ElectionBlackboxTests(WebTest): def test_get_election_raw(self): response = self.client.get("/helios/elections/%s" % self.election.uuid, follow=False) - self.assertEquals(response.content, self.election.toJSON()) + self.assertEqual(response.content, self.election.toJSON().encode('utf-8')) def test_get_election(self): response = self.client.get("/helios/elections/%s/view" % self.election.uuid, follow=False) @@ -460,7 +460,7 @@ class ElectionBlackboxTests(WebTest): def test_get_election_voters_raw(self): response = self.client.get("/helios/elections/%s/voters/" % self.election.uuid, follow=False) - self.assertEquals(len(utils.from_json(response.content)), self.election.num_voters) + self.assertEqual(len(utils.from_json(response.content)), self.election.num_voters) def test_election_creation_not_logged_in(self): response = self.client.post("/helios/elections/new", { @@ -489,7 +489,7 @@ class ElectionBlackboxTests(WebTest): self.assertRedirects(response, "/helios/elections/%s/view" % self.election.uuid) new_election = models.Election.objects.get(uuid = self.election.uuid) - self.assertEquals(new_election.short_name, self.election.short_name + "-2") + self.assertEqual(new_election.short_name, self.election.short_name + "-2") def test_get_election_stats(self): self.setup_login(from_scratch=True, user_id='mccio@github.com', user_type='google') @@ -569,7 +569,7 @@ class ElectionBlackboxTests(WebTest): # and we want to check that there are now voters response = self.client.get("/helios/elections/%s/voters/" % election_id) NUM_VOTERS = 4 - self.assertEquals(len(utils.from_json(response.content)), NUM_VOTERS) + self.assertEqual(len(utils.from_json(response.content)), NUM_VOTERS) # let's get a single voter single_voter = models.Election.objects.get(uuid = election_id).voter_set.all()[0] @@ -602,7 +602,7 @@ class ElectionBlackboxTests(WebTest): }) self.assertRedirects(response, "/helios/elections/%s/view" % election_id) num_messages_after = len(mail.outbox) - self.assertEquals(num_messages_after - num_messages_before, NUM_VOTERS) + self.assertEqual(num_messages_after - num_messages_before, NUM_VOTERS) email_message = mail.outbox[num_messages_before] assert "your password" in email_message.subject, "bad subject in email" @@ -613,7 +613,7 @@ class ElectionBlackboxTests(WebTest): # now log out as administrator self.clear_login() - self.assertEquals(self.client.session.has_key('user'), False) + self.assertEqual('user' in self.client.session, False) # return the voter username and password to vote return election_id, username, password @@ -629,7 +629,7 @@ class ElectionBlackboxTests(WebTest): # parse it as an encrypted vote with randomness, and make sure randomness is there the_ballot = utils.from_json(response.testbody) - assert the_ballot['answers'][0].has_key('randomness'), "no randomness" + assert 'randomness' in the_ballot['answers'][0], "no randomness" assert len(the_ballot['answers'][0]['randomness']) == 2, "not enough randomness" # parse it as an encrypted vote, and re-serialize it @@ -661,7 +661,7 @@ class ElectionBlackboxTests(WebTest): # confirm the vote, now with the actual form cast_form = cast_confirm_page.form - if 'status_update' in cast_form.fields.keys(): + if 'status_update' in list(cast_form.fields.keys()): cast_form['status_update'] = False response = cast_form.submit() @@ -711,7 +711,7 @@ class ElectionBlackboxTests(WebTest): self.assertRedirects(response, "/helios/elections/%s/view" % election_id) # should trigger helios decryption automatically - self.assertNotEquals(models.Election.objects.get(uuid=election_id).get_helios_trustee().decryption_proofs, None) + self.assertNotEqual(models.Election.objects.get(uuid=election_id).get_helios_trustee().decryption_proofs, None) # combine decryptions response = self.client.post("/helios/elections/%s/combine_decryptions" % election_id, { @@ -732,7 +732,7 @@ class ElectionBlackboxTests(WebTest): # check that tally matches response = self.client.get("/helios/elections/%s/result" % election_id) - self.assertEquals(utils.from_json(response.content), [[0,1]]) + self.assertEqual(utils.from_json(response.content), [[0,1]]) def test_do_complete_election(self): election_id, username, password = self._setup_complete_election() @@ -761,7 +761,7 @@ class ElectionBlackboxTests(WebTest): response = self.app.get("/helios/elections/%s/view" % election_id) # ensure it redirects - self.assertRedirects(response, "/helios/elections/%s/password_voter_login?%s" % (election_id, urllib.urlencode({"return_url": "/helios/elections/%s/view" % election_id}))) + self.assertRedirects(response, "/helios/elections/%s/password_voter_login?%s" % (election_id, urlencode({"return_url": "/helios/elections/%s/view" % election_id}))) login_form = response.follow().form diff --git a/helios/urls.py b/helios/urls.py index 6d278a9d195f17b169f4d7f126ef32ed3c012c61..9183eedd1fdc4e2c7fd89535b557d972106fc216 100644 --- a/helios/urls.py +++ b/helios/urls.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- from django.conf.urls import url, include -import url_names as names -import views +from . import views, url_names as names urlpatterns = [ url(r'^autologin$', views.admin_autologin), diff --git a/helios/utils.py b/helios/utils.py index 2ddc11c733f026a272b9b0f27b5cf54afae35bf2..0080c5f0a3d979d0efd6854f9e7304ec62bd43d1 100644 --- a/helios/utils.py +++ b/helios/utils.py @@ -5,14 +5,17 @@ Ben Adida - ben@adida.net 2005-04-11 """ -import urllib, re, datetime, string +import datetime +import re +import string +import urllib.parse + +from django.conf import settings from helios.crypto.utils import random # utils from helios_auth, too from helios_auth.utils import * -from django.conf import settings - def split_by_length(str, length, rejoin_with=None): """ @@ -37,7 +40,7 @@ def urlencode(str): if not str: return "" - return urllib.quote(str) + return urllib.parse.quote(str) def urlencodeall(str): """ @@ -52,11 +55,11 @@ def urldecode(str): if not str: return "" - return urllib.unquote(str) + return urllib.parse.unquote(str) def dictToURLParams(d): if d: - return '&'.join([i + '=' + urlencode(v) for i,v in d.items()]) + return '&'.join([i + '=' + urlencode(v) for i,v in list(d.items())]) else: return None ## @@ -86,22 +89,22 @@ def xss_strip_all_tags(s): if text[:2] == "&#": try: if text[:3] == "&#x": - return unichr(int(text[3:-1], 16)) + return chr(int(text[3:-1], 16)) else: - return unichr(int(text[2:-1])) + return chr(int(text[2:-1])) except ValueError: pass elif text[:1] == "&": - import htmlentitydefs - entity = htmlentitydefs.entitydefs.get(text[1:-1]) + import html.entities + entity = html.entities.entitydefs.get(text[1:-1]) if entity: if entity[:2] == "&#": try: - return unichr(int(entity[2:-1])) + return chr(int(entity[2:-1])) except ValueError: pass else: - return unicode(entity, "iso-8859-1") + return str(entity, "iso-8859-1") return text # leave as is return re.sub("(?s)<[^>]*>|&#?\w+;", fixup, s) @@ -127,7 +130,7 @@ def get_prefix(): ## def string_to_datetime(str, fmt="%Y-%m-%d %H:%M"): - if str == None: + if str is None: return None return datetime.datetime.strptime(str, fmt) diff --git a/helios/view_utils.py b/helios/view_utils.py index 26da7046c50d1f6c8d62e256cd67175f76cf141f..1c8c9a9011e44404633dcfd17e5706ce86bdb2b3 100644 --- a/helios/view_utils.py +++ b/helios/view_utils.py @@ -12,7 +12,7 @@ from django.template import loader from functools import update_wrapper import helios -import utils +from . import utils from helios_auth.security import get_user ## @@ -32,7 +32,7 @@ def prepare_vars(request, values): vars_with_user['user'] = get_user(request) # csrf protection - if request.session.has_key('csrf_token'): + if 'csrf_token' in request.session: vars_with_user['csrf_token'] = request.session['csrf_token'] vars_with_user['utils'] = utils @@ -67,7 +67,7 @@ def render_template_raw(request, template_name, values=None): def render_json(json_txt): - return HttpResponse(json_txt, "application/json") + return HttpResponse(utils.to_json(json_txt), content_type="application/json") # decorator @@ -79,8 +79,8 @@ def return_json(func): def convert_to_json(self, *args, **kwargs): return_val = func(self, *args, **kwargs) try: - return render_json(utils.to_json(return_val)) - except Exception, e: + return render_json(return_val) + except Exception as e: import logging logging.error("problem with serialization: " + str(return_val) + " / " + str(e)) raise e diff --git a/helios/views.py b/helios/views.py index b62f6eebf73f14a748fdcd84a61d109394fb263d..ac52823a85e8f54cef45e7bbdec345e2f02d565a 100644 --- a/helios/views.py +++ b/helios/views.py @@ -5,51 +5,46 @@ Helios Django Views Ben Adida (ben@adida.net) """ -from django.urls import reverse -from django.core.paginator import Paginator +import base64 +import datetime +import logging +import os +import uuid +from urllib.parse import urlencode + from django.core.exceptions import PermissionDenied -from django.http import HttpResponse, Http404, HttpResponseRedirect, HttpResponseForbidden +from django.core.paginator import Paginator from django.db import transaction, IntegrityError - +from django.http import HttpResponse, Http404, HttpResponseRedirect, HttpResponseForbidden +from django.urls import reverse from validate_email import validate_email -import urllib, os, base64 - -from crypto import algs, electionalgs, elgamal -from crypto import utils as cryptoutils -from workflows import homomorphic +import helios_auth.url_names as helios_auth_urls from helios import utils, VOTERS_EMAIL, VOTERS_UPLOAD, url_names -from view_utils import SUCCESS, FAILURE, return_json, render_template, render_template_raw - -from helios_auth.security import check_csrf, login_required, get_user, save_in_session_across_logouts +from helios_auth import views as auth_views from helios_auth.auth_systems import AUTH_SYSTEMS, can_list_categories from helios_auth.models import AuthenticationExpired -import helios_auth.url_names as helios_auth_urls - -from helios_auth import views as auth_views - -import tasks - -from security import (election_view, election_admin, - trustee_check, set_logged_in_trustee, - can_create_election, user_can_see_election, get_voter, - user_can_admin_election, user_can_feature_election) - -import uuid, datetime -import logging - -from models import User, Election, CastVote, Voter, VoterFile, Trustee, AuditedBallot -import datatypes - -import forms +from helios_auth.security import check_csrf, login_required, get_user, save_in_session_across_logouts +from . import datatypes +from . import forms +from . import tasks +from .crypto import algs, electionalgs, elgamal +from .crypto import utils as cryptoutils +from .models import User, Election, CastVote, Voter, VoterFile, Trustee, AuditedBallot +from .security import (election_view, election_admin, + trustee_check, set_logged_in_trustee, + can_create_election, user_can_see_election, get_voter, + user_can_admin_election, user_can_feature_election) +from .view_utils import SUCCESS, FAILURE, return_json, render_template, render_template_raw +from .workflows import homomorphic # Parameters for everything ELGAMAL_PARAMS = elgamal.Cryptosystem() # trying new ones from OlivierP -ELGAMAL_PARAMS.p = 16328632084933010002384055033805457329601614771185955389739167309086214800406465799038583634953752941675645562182498120750264980492381375579367675648771293800310370964745767014243638518442553823973482995267304044326777047662957480269391322789378384619428596446446984694306187644767462460965622580087564339212631775817895958409016676398975671266179637898557687317076177218843233150695157881061257053019133078545928983562221396313169622475509818442661047018436264806901023966236718367204710755935899013750306107738002364137917426595737403871114187750804346564731250609196846638183903982387884578266136503697493474682071L -ELGAMAL_PARAMS.q = 61329566248342901292543872769978950870633559608669337131139375508370458778917L -ELGAMAL_PARAMS.g = 14887492224963187634282421537186040801304008017743492304481737382571933937568724473847106029915040150784031882206090286938661464458896494215273989547889201144857352611058572236578734319505128042602372864570426550855201448111746579871811249114781674309062693442442368697449970648232621880001709535143047913661432883287150003429802392229361583608686643243349727791976247247948618930423866180410558458272606627111270040091203073580238905303994472202930783207472394578498507764703191288249547659899997131166130259700604433891232298182348403175947450284433411265966789131024573629546048637848902243503970966798589660808533L +ELGAMAL_PARAMS.p = 16328632084933010002384055033805457329601614771185955389739167309086214800406465799038583634953752941675645562182498120750264980492381375579367675648771293800310370964745767014243638518442553823973482995267304044326777047662957480269391322789378384619428596446446984694306187644767462460965622580087564339212631775817895958409016676398975671266179637898557687317076177218843233150695157881061257053019133078545928983562221396313169622475509818442661047018436264806901023966236718367204710755935899013750306107738002364137917426595737403871114187750804346564731250609196846638183903982387884578266136503697493474682071 +ELGAMAL_PARAMS.q = 61329566248342901292543872769978950870633559608669337131139375508370458778917 +ELGAMAL_PARAMS.g = 14887492224963187634282421537186040801304008017743492304481737382571933937568724473847106029915040150784031882206090286938661464458896494215273989547889201144857352611058572236578734319505128042602372864570426550855201448111746579871811249114781674309062693442442368697449970648232621880001709535143047913661432883287150003429802392229361583608686643243349727791976247247948618930423866180410558458272606627111270040091203073580238905303994472202930783207472394578498507764703191288249547659899997131166130259700604433891232298182348403175947450284433411265966789131024573629546048637848902243503970966798589660808533 # object ready for serialization ELGAMAL_PARAMS_LD_OBJECT = datatypes.LDObject.instantiate(ELGAMAL_PARAMS, datatype='legacy/EGParams') @@ -78,7 +73,7 @@ def user_reauth(request, user): # add a parameter to prevent it? Maybe. login_url = "%s%s?%s" % (settings.SECURE_URL_HOST, reverse(helios_auth_urls.AUTH_START, args=[user.user_type]), - urllib.urlencode({'return_url': + urlencode({'return_url': request.get_full_path()})) return HttpResponseRedirect(login_url) @@ -124,9 +119,9 @@ def election_shortcut(request, election_short_name): # a hidden view behind the shortcut that performs the actual perm check @election_view() def _election_vote_shortcut(request, election): - vote_url = "%s/booth/vote.html?%s" % (settings.SECURE_URL_HOST, urllib.urlencode({'election_url' : reverse(url_names.election.ELECTION_HOME, args=[election.uuid])})) + vote_url = "%s/booth/vote.html?%s" % (settings.SECURE_URL_HOST, urlencode({'election_url' : reverse(url_names.election.ELECTION_HOME, args=[election.uuid])})) - test_cookie_url = "%s?%s" % (reverse(url_names.COOKIE_TEST), urllib.urlencode({'continue_url' : vote_url})) + test_cookie_url = "%s?%s" % (reverse(url_names.COOKIE_TEST), urlencode({'continue_url' : vote_url})) return HttpResponseRedirect(test_cookie_url) @@ -304,9 +299,9 @@ def one_election_view(request, election): election_badge_url = get_election_badge_url(election) status_update_message = None - vote_url = "%s/booth/vote.html?%s" % (settings.SECURE_URL_HOST, urllib.urlencode({'election_url' : reverse(url_names.election.ELECTION_HOME, args=[election.uuid])})) + vote_url = "%s/booth/vote.html?%s" % (settings.SECURE_URL_HOST, urlencode({'election_url' : reverse(url_names.election.ELECTION_HOME, args=[election.uuid])})) - test_cookie_url = "%s?%s" % (reverse(url_names.COOKIE_TEST), urllib.urlencode({'continue_url' : vote_url})) + test_cookie_url = "%s?%s" % (reverse(url_names.COOKIE_TEST), urlencode({'continue_url' : vote_url})) if user: voter = Voter.get_by_election_and_user(election, user) @@ -329,13 +324,13 @@ def one_election_view(request, election): # status update message? if election.openreg: if election.voting_has_started: - status_update_message = u"Vote in %s" % election.name + status_update_message = "Vote in %s" % election.name else: - status_update_message = u"Register to vote in %s" % election.name + status_update_message = "Register to vote in %s" % election.name # result! if election.result: - status_update_message = u"Results are in for %s" % election.name + status_update_message = "Results are in for %s" % election.name trustees = Trustee.get_by_election(election) @@ -353,20 +348,20 @@ def one_election_view(request, election): def test_cookie(request): continue_url = request.GET['continue_url'] request.session.set_test_cookie() - next_url = "%s?%s" % (reverse(url_names.COOKIE_TEST_2), urllib.urlencode({'continue_url': continue_url})) + next_url = "%s?%s" % (reverse(url_names.COOKIE_TEST_2), urlencode({'continue_url': continue_url})) return HttpResponseRedirect(settings.SECURE_URL_HOST + next_url) def test_cookie_2(request): continue_url = request.GET['continue_url'] if not request.session.test_cookie_worked(): - return HttpResponseRedirect(settings.SECURE_URL_HOST + ("%s?%s" % (reverse(url_names.COOKIE_NO), urllib.urlencode({'continue_url': continue_url})))) + return HttpResponseRedirect(settings.SECURE_URL_HOST + ("%s?%s" % (reverse(url_names.COOKIE_NO), urlencode({'continue_url': continue_url})))) request.session.delete_test_cookie() return HttpResponseRedirect(continue_url) def nocookies(request): - retest_url = "%s?%s" % (reverse(url_names.COOKIE_TEST), urllib.urlencode({'continue_url' : request.GET['continue_url']})) + retest_url = "%s?%s" % (reverse(url_names.COOKIE_TEST), urlencode({'continue_url' : request.GET['continue_url']})) return render_template(request, 'nocookies', {'retest_url': retest_url}) ## @@ -593,7 +588,7 @@ def password_voter_login(request, election): return one_election_cast_confirm(request, election.uuid) except Voter.DoesNotExist: - redirect_url = login_url + "?" + urllib.urlencode({ + redirect_url = login_url + "?" + urlencode({ 'bad_voter_login' : '1', 'return_url' : return_url }) @@ -601,7 +596,7 @@ def password_voter_login(request, election): return HttpResponseRedirect(settings.SECURE_URL_HOST + redirect_url) else: # bad form, bad voter login - redirect_url = login_url + "?" + urllib.urlencode({ + redirect_url = login_url + "?" + urlencode({ 'bad_voter_login' : '1', 'return_url' : return_url }) @@ -615,7 +610,7 @@ def one_election_cast_confirm(request, election): user = get_user(request) # if no encrypted vote, the user is reloading this page or otherwise getting here in a bad way - if (not request.session.has_key('encrypted_vote')) or request.session['encrypted_vote'] == None: + if ('encrypted_vote' not in request.session) or request.session['encrypted_vote'] is None: return HttpResponseRedirect(settings.URL_HOST) # election not frozen or started @@ -693,7 +688,7 @@ def one_election_cast_confirm(request, election): password_only = False - if auth_systems == None or 'password' in auth_systems: + if auth_systems is None or 'password' in auth_systems: show_password = True password_login_form = forms.VoterPasswordForm() @@ -763,7 +758,7 @@ def one_election_cast_done(request, election): # only log out if the setting says so *and* we're dealing # with a site-wide voter. Definitely remove current_voter # checking that voter.user != None is needed because voter.user may now be None if voter is password only - if voter.user == user and voter.user != None: + if voter.user == user and voter.user is not None: logout = settings.LOGOUT_ON_CONFIRMATION else: logout = False @@ -819,7 +814,7 @@ def one_election_bboard(request, election): order_by = 'alias' # if there's a specific voter - if request.GET.has_key('q'): + if 'q' in request.GET: # FIXME: figure out the voter by voter_id voters = [] else: @@ -843,7 +838,7 @@ def one_election_audited_ballots(request, election): UI to show election audited ballots """ - if request.GET.has_key('vote_hash'): + if 'vote_hash' in request.GET: b = AuditedBallot.get(election, request.GET['vote_hash']) return HttpResponse(b.raw_vote, content_type="text/plain") @@ -1082,14 +1077,14 @@ def one_election_compute_tally(request, election): @trustee_check def trustee_decrypt_and_prove(request, election, trustee): - if not _check_election_tally_type(election) or election.encrypted_tally == None: + if not _check_election_tally_type(election) or election.encrypted_tally is None: return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW,args=[election.uuid])) return render_template(request, 'trustee_decrypt_and_prove', {'election': election, 'trustee': trustee}) @election_view(frozen=True) def trustee_upload_decryption(request, election, trustee_uuid): - if not _check_election_tally_type(election) or election.encrypted_tally == None: + if not _check_election_tally_type(election) or election.encrypted_tally is None: return FAILURE trustee = Trustee.get_by_election_and_uuid(election, trustee_uuid) @@ -1130,7 +1125,7 @@ def release_result(request, election): election.save() if request.POST.get('send_email', ''): - return HttpResponseRedirect("%s?%s" % (settings.SECURE_URL_HOST + reverse(voters_email, args=[election.uuid]),urllib.urlencode({'template': 'result'}))) + return HttpResponseRedirect("%s?%s" % (settings.SECURE_URL_HOST + reverse(voters_email, args=[election.uuid]),urlencode({'template': 'result'}))) else: return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid])) @@ -1293,7 +1288,7 @@ def voters_upload(request, election): return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(voters_list_pretty, args=[election.uuid])) else: # we need to confirm - if request.FILES.has_key('voters_file'): + if 'voters_file' in request.FILES: voters_file = request.FILES['voters_file'] voter_file_obj = election.add_voters_file(voters_file) @@ -1314,7 +1309,7 @@ def voters_upload(request, election): return render_template(request, 'voters_upload_confirm', {'election': election, 'voters': voters, 'problems': problems}) else: - return HttpResponseRedirect("%s?%s" % (settings.SECURE_URL_HOST + reverse(voters_upload, args=[election.uuid]), urllib.urlencode({'e':'no voter file specified, try again'}))) + return HttpResponseRedirect("%s?%s" % (settings.SECURE_URL_HOST + reverse(voters_upload, args=[election.uuid]), urlencode({'e':'no voter file specified, try again'}))) @election_admin() def voters_upload_cancel(request, election): @@ -1472,9 +1467,9 @@ def ballot_list(request, election): and optionally take a after parameter. """ limit = after = None - if request.GET.has_key('limit'): + if 'limit' in request.GET: limit = int(request.GET['limit']) - if request.GET.has_key('after'): + if 'after' in request.GET: after = datetime.datetime.strptime(request.GET['after'], '%Y-%m-%d %H:%M:%S') voters = Voter.get_by_election(election, cast=True, order_by='cast_at', limit=limit, after=after) diff --git a/helios/widgets.py b/helios/widgets.py index 9eff2f435bc1bf7e37cb8174a5bc1a6db12272f1..e4855a69b6e6b8194e3bd1575079cfc9063c4647 100644 --- a/helios/widgets.py +++ b/helios/widgets.py @@ -54,18 +54,18 @@ class SelectTimeWidget(Widget): self.meridiem_val = 'a.m.' # Default to Morning (A.M.) if hour_step and twelve_hr: - self.hours = range(1,13,hour_step) + self.hours = list(range(1,13,hour_step)) elif hour_step: # 24hr, with stepping. - self.hours = range(0,24,hour_step) + self.hours = list(range(0,24,hour_step)) elif twelve_hr: # 12hr, no stepping - self.hours = range(1,13) + self.hours = list(range(1,13)) else: # 24hr, no stepping - self.hours = range(0,24) + self.hours = list(range(0,24)) if minute_step: - self.minutes = range(0,60,minute_step) + self.minutes = list(range(0,60,minute_step)) else: - self.minutes = range(0,60) + self.minutes = list(range(0,60)) def render(self, name, value, attrs=None, renderer=None): try: # try to get time values from a datetime.time object (value) @@ -77,7 +77,7 @@ class SelectTimeWidget(Widget): self.meridiem_val = 'a.m.' except AttributeError: hour_val = minute_val = 0 - if isinstance(value, basestring): + if isinstance(value, str): match = RE_TIME.match(value) if match: time_groups = match.groups() @@ -113,8 +113,8 @@ class SelectTimeWidget(Widget): # For times to get displayed correctly, the values MUST be converted to unicode # When Select builds a list of options, it checks against Unicode values - hour_val = u"%.2d" % hour_val - minute_val = u"%.2d" % minute_val + hour_val = "%.2d" % hour_val + minute_val = "%.2d" % minute_val hour_choices = [("%.2d"%i, "%.2d"%i) for i in self.hours] local_attrs = self.build_attrs({'id': self.hour_field % id_}) @@ -137,7 +137,7 @@ class SelectTimeWidget(Widget): select_html = Select(choices=meridiem_choices).render(self.meridiem_field % name, self.meridiem_val, local_attrs) output.append(select_html) - return mark_safe(u'\n'.join(output)) + return mark_safe('\n'.join(output)) def id_for_label(self, id_): return '%s_hour' % id_ @@ -189,4 +189,4 @@ class SplitSelectDateTimeWidget(MultiWidget): def render(self, name, value, attrs=None, renderer=None): rendered_widgets = list(widget.render(name, value, attrs=attrs, renderer=renderer) for widget in self.widgets) - return u'<br/>'.join(rendered_widgets) + return '<br/>'.join(rendered_widgets) diff --git a/helios/workflows/homomorphic.py b/helios/workflows/homomorphic.py index 9becf335a1654fc1448385266bd106fd9b57c945..2f03f50ecc6375be6adcc966e350e5c4a1a84924 100644 --- a/helios/workflows/homomorphic.py +++ b/helios/workflows/homomorphic.py @@ -68,10 +68,10 @@ class EncryptedAnswer(WorkflowObject): return False # compute homomorphic sum if needed - if max != None: + if max is not None: homomorphic_sum = choice * homomorphic_sum - if max != None: + if max is not None: # determine possible plaintexts for the sum sum_possible_plaintexts = self.generate_plaintexts(pk, min=min, max=max) @@ -110,7 +110,7 @@ class EncryptedAnswer(WorkflowObject): # min and max for number of answers, useful later min_answers = 0 - if question.has_key('min'): + if 'min' in question: min_answers = question['min'] max_answers = question['max'] @@ -132,7 +132,7 @@ class EncryptedAnswer(WorkflowObject): randomness[answer_num], algs.EG_disjunctive_challenge_generator) # sum things up homomorphically if needed - if max_answers != None: + if max_answers is not None: homomorphic_sum = choices[answer_num] * homomorphic_sum randomness_sum = (randomness_sum + randomness[answer_num]) % pk.q @@ -142,7 +142,7 @@ class EncryptedAnswer(WorkflowObject): if num_selected_answers < min_answers: raise Exception("Need to select at least %s answer(s)" % min_answers) - if max_answers != None: + 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 @@ -195,7 +195,7 @@ class EncryptedVote(WorkflowObject): question = election.questions[question_num] min_answers = 0 - if question.has_key('min'): + if 'min' in question: min_answers = question['min'] if not ea.verify(election.public_key, min=min_answers, max=question['max']): diff --git a/helios_auth/__init__.py b/helios_auth/__init__.py index ca245ff38f820db528afbed3a43d597bc8642b44..2c9c090a97a242a71b7d3d6c177dfa31d3016476 100644 --- a/helios_auth/__init__.py +++ b/helios_auth/__init__.py @@ -4,7 +4,7 @@ from django.conf import settings TEMPLATE_BASE = settings.AUTH_TEMPLATE_BASE or "helios_auth/templates/base.html" # enabled auth systems -import auth_systems -ENABLED_AUTH_SYSTEMS = settings.AUTH_ENABLED_AUTH_SYSTEMS or auth_systems.AUTH_SYSTEMS.keys() +from . import auth_systems +ENABLED_AUTH_SYSTEMS = settings.AUTH_ENABLED_AUTH_SYSTEMS or list(auth_systems.AUTH_SYSTEMS.keys()) DEFAULT_AUTH_SYSTEM = settings.AUTH_DEFAULT_AUTH_SYSTEM or None diff --git a/helios_auth/auth_systems/__init__.py b/helios_auth/auth_systems/__init__.py index 5a0e9233ba4df89067688283e90649aef4f1ae70..07ebaa867a322cdfeeb133a9713731b2f02a44fa 100644 --- a/helios_auth/auth_systems/__init__.py +++ b/helios_auth/auth_systems/__init__.py @@ -1,15 +1,42 @@ +from django.conf import settings + +_enabled = settings.AUTH_ENABLED_AUTH_SYSTEMS or None +def _is_enabled(system): + return _enabled is not None or system in _enabled AUTH_SYSTEMS = {} -import twitter, password, cas, facebook, google, yahoo, linkedin, clever -AUTH_SYSTEMS['twitter'] = twitter -AUTH_SYSTEMS['linkedin'] = linkedin -AUTH_SYSTEMS['password'] = password -AUTH_SYSTEMS['cas'] = cas -AUTH_SYSTEMS['facebook'] = facebook -AUTH_SYSTEMS['google'] = google -AUTH_SYSTEMS['yahoo'] = yahoo -AUTH_SYSTEMS['clever'] = clever +if _is_enabled('twitter'): + from . import twitter + AUTH_SYSTEMS['twitter'] = twitter + +if _is_enabled('linkedin'): + from . import linkedin + AUTH_SYSTEMS['linkedin'] = linkedin + +if _is_enabled('password'): + from . import password + AUTH_SYSTEMS['password'] = password + +if _is_enabled('cas'): + from . import cas + AUTH_SYSTEMS['cas'] = cas + +if _is_enabled('facebook'): + from . import facebook + AUTH_SYSTEMS['facebook'] = facebook + +if _is_enabled('google'): + from . import google + AUTH_SYSTEMS['google'] = google + +if _is_enabled('yahoo'): + from . import yahoo + AUTH_SYSTEMS['yahoo'] = yahoo + +if _is_enabled('clever'): + from . import clever + AUTH_SYSTEMS['clever'] = clever # not ready #import live diff --git a/helios_auth/auth_systems/cas.py b/helios_auth/auth_systems/cas.py index dd4216b5ecc2c3953892332fa49ae8a51b13eeff..21c86cfe73577021491bacfd1c20f58f36c94fe2 100644 --- a/helios_auth/auth_systems/cas.py +++ b/helios_auth/auth_systems/cas.py @@ -7,13 +7,14 @@ https://sp.princeton.edu/oit/sdp/CAS/Wiki%20Pages/Python.aspx import datetime import re -import urllib -import urllib2 +import urllib.parse +import urllib.request import uuid +from xml.etree import ElementTree + from django.conf import settings from django.core.mail import send_mail from django.http import HttpResponseRedirect -from xml.etree import ElementTree CAS_EMAIL_DOMAIN = "princeton.edu" CAS_URL= 'https://fed.princeton.edu/cas/' @@ -42,17 +43,17 @@ def _get_service_url(): def get_auth_url(request, redirect_url): request.session['cas_redirect_url'] = redirect_url - return CAS_URL + 'login?service=' + urllib.quote(_get_service_url()) + return CAS_URL + 'login?service=' + urllib.parse.quote(_get_service_url()) def get_user_category(user_id): theurl = CAS_ELIGIBILITY_URL % user_id - auth_handler = urllib2.HTTPBasicAuthHandler() + auth_handler = urllib.request.HTTPBasicAuthHandler() auth_handler.add_password(realm=CAS_ELIGIBILITY_REALM, uri= theurl, user= CAS_USERNAME, passwd = CAS_PASSWORD) - opener = urllib2.build_opener(auth_handler) - urllib2.install_opener(opener) + opener = urllib.request.build_opener(auth_handler) + urllib.request.install_opener(opener) - result = urllib2.urlopen(CAS_ELIGIBILITY_URL % user_id).read().strip() + result = urllib.request.urlopen(CAS_ELIGIBILITY_URL % user_id).read().strip() parsed_result = ElementTree.fromstring(result) return parsed_result.text @@ -78,11 +79,11 @@ def get_saml_info(ticket): </soap-env:Envelope> """ % (uuid.uuid1(), datetime.datetime.utcnow().isoformat(), ticket) - url = CAS_SAML_VALIDATE_URL % urllib.quote(_get_service_url()) + url = CAS_SAML_VALIDATE_URL % urllib.parse.quote(_get_service_url()) # by virtue of having a body, this is a POST - req = urllib2.Request(url, saml_request) - raw_response = urllib2.urlopen(req).read() + req = urllib.request.Request(url, saml_request) + raw_response = urllib.request.urlopen(req).read() logging.info("RESP:\n%s\n\n" % raw_response) @@ -130,8 +131,8 @@ def get_user_info(user_id): </soap-env:Envelope> """ % user_id - req = urllib2.Request(url, request_body, headers) - response = urllib2.urlopen(req).read() + req = urllib.request.Request(url, request_body, headers) + response = urllib.request.urlopen(req).read() # parse the result from xml.dom.minidom import parseString @@ -149,12 +150,12 @@ def get_user_info(user_id): def get_user_info_special(ticket): # fetch the information from the CAS server val_url = CAS_URL + "validate" + \ - '?service=' + urllib.quote(_get_service_url()) + \ - '&ticket=' + urllib.quote(ticket) - r = urllib.urlopen(val_url).readlines() # returns 2 lines + '?service=' + urllib.parse.quote(_get_service_url()) + \ + '&ticket=' + urllib.parse.quote(ticket) + r = urllib.request.urlopen(val_url).readlines() # returns 2 lines # success - if len(r) == 2 and re.match("yes", r[0]) != None: + if len(r) == 2 and re.match("yes", r[0]) is not None: netid = r[1].strip() category = get_user_category(netid) @@ -212,7 +213,7 @@ def send_message(user_id, name, user_info, subject, body): else: email = "%s@%s" % (user_id, CAS_EMAIL_DOMAIN) - if user_info.has_key('name'): + if 'name' in user_info: name = user_info["name"] else: name = email @@ -224,7 +225,7 @@ def send_message(user_id, name, user_info, subject, body): # def check_constraint(constraint, user): - if not user.info.has_key('category'): + if 'category' not in user.info: return False return constraint['year'] == user.info['category'] diff --git a/helios_auth/auth_systems/clever.py b/helios_auth/auth_systems/clever.py index 498951f8dbf0e3d60f2460c83d580be0b703bb64..48648a0b07a55add763512be3beb457ed08bb298 100644 --- a/helios_auth/auth_systems/clever.py +++ b/helios_auth/auth_systems/clever.py @@ -4,12 +4,14 @@ Clever Authentication """ import base64 +import urllib.parse + import httplib2 -import json -import urllib from django.conf import settings from oauth2client.client import OAuth2WebServerFlow, OAuth2Credentials +from helios_auth import utils + # some parameters to indicate that status updating is not possible STATUS_UPDATES = False @@ -42,7 +44,7 @@ def get_user_info_after_auth(request): # do the POST manually, because OAuth2WebFlow can't do auth header for token exchange http = httplib2.Http(".cache") auth_header = "Basic %s" % base64.b64encode(settings.CLEVER_CLIENT_ID + ":" + settings.CLEVER_CLIENT_SECRET) - resp_headers, content = http.request("https://clever.com/oauth/tokens", "POST", urllib.urlencode({ + resp_headers, content = http.request("https://clever.com/oauth/tokens", "POST", urllib.parse.urlencode({ "code" : code, "grant_type": "authorization_code", "redirect_uri": redirect_uri @@ -51,7 +53,7 @@ def get_user_info_after_auth(request): 'Content-Type': "application/x-www-form-urlencoded" }) - token_response = json.loads(content) + token_response = utils.from_json(content) access_token = token_response['access_token'] # package the credentials @@ -62,7 +64,7 @@ def get_user_info_after_auth(request): (resp_headers, content) = http.request("https://api.clever.com/me", "GET") # {"type":"student","data":{"id":"563395179f7408755c0006b7","district":"5633941748c07c0100000aac","type":"student","created":"2015-10-30T16:04:39.262Z","credentials":{"district_password":"eel7Thohd","district_username":"dianes10"},"dob":"1998-11-01T00:00:00.000Z","ell_status":"Y","email":"diane.s@example.org","gender":"F","grade":"9","hispanic_ethnicity":"Y","last_modified":"2015-10-30T16:04:39.274Z","location":{"zip":"11433"},"name":{"first":"Diane","last":"Schmeler","middle":"J"},"race":"Asian","school":"5633950c62fc41c041000005","sis_id":"738733110","state_id":"114327752","student_number":"738733110"},"links":[{"rel":"self","uri":"/me"},{"rel":"canonical","uri":"/v1.1/students/563395179f7408755c0006b7"},{"rel":"district","uri":"/v1.1/districts/5633941748c07c0100000aac"}]} - response = json.loads(content) + response = utils.from_json(content) user_id = response['data']['id'] user_name = "%s %s" % (response['data']['name']['first'], response['data']['name']['last']) @@ -70,7 +72,7 @@ def get_user_info_after_auth(request): user_district = response['data']['district'] user_grade = response['data'].get('grade', None) - print content + print(content) # watch out, response also contains email addresses, but not sure whether thsoe are verified or not # so for email address we will only look at the id_token @@ -100,7 +102,7 @@ def send_message(user_id, name, user_info, subject, body): # def check_constraint(constraint, user): - if not user.info.has_key('grade'): + if 'grade' not in user.info: return False return constraint['grade'] == user.info['grade'] diff --git a/helios_auth/auth_systems/facebook.py b/helios_auth/auth_systems/facebook.py index 4c50c6594336f60c2999303c57088e6feeb77b4c..4810ec20494fef34aba4e5017d092c88e185c39c 100644 --- a/helios_auth/auth_systems/facebook.py +++ b/helios_auth/auth_systems/facebook.py @@ -2,8 +2,6 @@ Facebook Authentication """ -import logging - from django.conf import settings from django.core.mail import send_mail @@ -12,7 +10,7 @@ API_KEY = settings.FACEBOOK_API_KEY API_SECRET = settings.FACEBOOK_API_SECRET #from facebookclient import Facebook -import urllib, urllib2, cgi +import urllib.request, urllib.error, urllib.parse # some parameters to indicate that status updating is possible STATUS_UPDATES = True @@ -22,21 +20,21 @@ from helios_auth import utils def facebook_url(url, params): if params: - return "https://graph.facebook.com%s?%s" % (url, urllib.urlencode(params)) + return "https://graph.facebook.com%s?%s" % (url, urllib.parse.urlencode(params)) else: return "https://graph.facebook.com%s" % url def facebook_get(url, params): full_url = facebook_url(url,params) try: - return urllib2.urlopen(full_url).read() - except urllib2.HTTPError: + return urllib.request.urlopen(full_url).read() + except urllib.error.HTTPError: from helios_auth.models import AuthenticationExpired raise AuthenticationExpired() def facebook_post(url, params): full_url = facebook_url(url, None) - return urllib2.urlopen(full_url, urllib.urlencode(params)).read() + return urllib.request.urlopen(full_url, urllib.parse.urlencode(params)).read() def get_auth_url(request, redirect_url): request.session['fb_redirect_uri'] = redirect_url @@ -69,7 +67,7 @@ def update_status(user_id, user_info, token, message): }) def send_message(user_id, user_name, user_info, subject, body): - if user_info.has_key('email'): + if 'email' in user_info: send_mail(subject, body, settings.SERVER_EMAIL, ["%s <%s>" % (user_name, user_info['email'])], fail_silently=False) diff --git a/helios_auth/auth_systems/facebookclient/__init__.py b/helios_auth/auth_systems/facebookclient/__init__.py index 03264730c9ab284b3500793fc232ad15175a945e..df8fe2233d47ba65e3d093a819b3cbb850f92eaa 100644 --- a/helios_auth/auth_systems/facebookclient/__init__.py +++ b/helios_auth/auth_systems/facebookclient/__init__.py @@ -44,16 +44,15 @@ http://undefined.org/python/#simplejson to download it, or do apt-get install python-simplejson on a Debian-like system. """ -import sys -import time -import struct -import urllib -import urllib2 -import httplib -import hashlib import binascii -import urlparse +import hashlib +import http.client import mimetypes +import struct +import time +import urllib.error +import urllib.parse +import urllib.request # try to use simplejson first, otherwise fallback to XML RESPONSE_FORMAT = 'JSON' @@ -96,11 +95,11 @@ try: if result.status_code == 200: return result.content else: - raise urllib2.URLError("fetch error url=%s, code=%d" % (url, result.status_code)) + raise urllib.error.URLError("fetch error url=%s, code=%d" % (url, result.status_code)) except ImportError: def urlread(url, data=None): - res = urllib2.urlopen(url, data=data) + res = urllib.request.urlopen(url, data=data) return res.read() __all__ = ['Facebook'] @@ -711,9 +710,9 @@ class PhotosProxy(PhotosProxy): args = self._client._build_post_args('facebook.photos.upload', self._client._add_session_args(args)) try: - import cStringIO as StringIO + import io as StringIO except ImportError: - import StringIO + import io # check for a filename specified...if the user is passing binary data in # image then a filename will be specified @@ -721,26 +720,26 @@ class PhotosProxy(PhotosProxy): try: import Image except ImportError: - data = StringIO.StringIO(open(image, 'rb').read()) + data = io.StringIO(open(image, 'rb').read()) else: img = Image.open(image) if size: img.thumbnail(size, Image.ANTIALIAS) - data = StringIO.StringIO() + data = io.StringIO() img.save(data, img.format) else: # there was a filename specified, which indicates that image was not # the path to an image file but rather the binary data of a file - data = StringIO.StringIO(image) + data = io.StringIO(image) image = filename - content_type, body = self.__encode_multipart_formdata(list(args.iteritems()), [(image, data)]) - urlinfo = urlparse.urlsplit(self._client.facebook_url) + content_type, body = self.__encode_multipart_formdata(list(args.items()), [(image, data)]) + urlinfo = urllib.parse.urlsplit(self._client.facebook_url) try: content_length = len(body) chunk_size = 4096 - h = httplib.HTTPConnection(urlinfo[1]) + h = http.client.HTTPConnection(urlinfo[1]) h.putrequest('POST', urlinfo[2]) h.putheader('Content-Type', content_type) h.putheader('Content-Length', str(content_length)) @@ -776,7 +775,7 @@ class PhotosProxy(PhotosProxy): try: response = urlread(url=self._client.facebook_url,data=body,headers={'POST':urlinfo[2],'Content-Type':content_type,'MIME-Version':'1.0'}) - except urllib2.URLError: + except urllib.error.URLError: raise Exception('Error uploading photo: Facebook returned %s' % (response)) except ImportError: # could not import from google.appengine.api, so we are not running in GAE @@ -954,7 +953,7 @@ class Facebook(object): """Hashes arguments by joining key=value pairs, appending a secret, and then taking the MD5 hex digest.""" # @author: houyr # fix for UnicodeEncodeError - hasher = hashlib.md5(''.join(['%s=%s' % (isinstance(x, unicode) and x.encode("utf-8") or x, isinstance(args[x], unicode) and args[x].encode("utf-8") or args[x]) for x in sorted(args.keys())])) + hasher = hashlib.md5(''.join(['%s=%s' % (isinstance(x, str) and x.encode("utf-8") or x, isinstance(args[x], str) and args[x].encode("utf-8") or args[x]) for x in sorted(args.keys())])) if secret: hasher.update(secret) elif self.secret: @@ -976,7 +975,7 @@ class Facebook(object): node.hasAttribute('list') and \ node.getAttribute('list')=="true": return self._parse_response_list(node) - elif len(filter(lambda x: x.nodeType == x.ELEMENT_NODE, node.childNodes)) > 0: + elif len([x for x in node.childNodes if x.nodeType == x.ELEMENT_NODE]) > 0: return self._parse_response_dict(node) else: return ''.join(node.data for node in node.childNodes if node.nodeType == node.TEXT_NODE) @@ -985,7 +984,7 @@ class Facebook(object): def _parse_response_dict(self, node): """Parses an XML dictionary response node from Facebook.""" result = {} - for item in filter(lambda x: x.nodeType == x.ELEMENT_NODE, node.childNodes): + for item in [x for x in node.childNodes if x.nodeType == x.ELEMENT_NODE]: result[item.nodeName] = self._parse_response_item(item) if node.nodeType == node.ELEMENT_NODE and node.hasAttributes(): if node.hasAttribute('id'): @@ -996,14 +995,14 @@ class Facebook(object): def _parse_response_list(self, node): """Parses an XML list response node from Facebook.""" result = [] - for item in filter(lambda x: x.nodeType == x.ELEMENT_NODE, node.childNodes): + for item in [x for x in node.childNodes if x.nodeType == x.ELEMENT_NODE]: result.append(self._parse_response_item(item)) return result def _check_error(self, response): """Checks if the given Facebook response is an error, and then raises the appropriate exception.""" - if type(response) is dict and response.has_key('error_code'): + if isinstance(response, dict) and 'error_code' in response: raise FacebookError(response['error_code'], response['error_msg'], response['request_args']) @@ -1012,12 +1011,12 @@ class Facebook(object): if args is None: args = {} - for arg in args.items(): - if type(arg[1]) == list: + for arg in list(args.items()): + if isinstance(arg[1], list): args[arg[0]] = ','.join(str(a) for a in arg[1]) - elif type(arg[1]) == unicode: + elif isinstance(arg[1], str): args[arg[0]] = arg[1].encode("UTF-8") - elif type(arg[1]) == bool: + elif isinstance(arg[1], bool): args[arg[0]] = str(arg[1]).lower() args['method'] = method @@ -1087,8 +1086,8 @@ class Facebook(object): A unicode aware version of urllib.urlencode. """ if isinstance(params, dict): - params = params.items() - return urllib.urlencode([(k, isinstance(v, unicode) and v.encode('utf-8') or v) + params = list(params.items()) + return urllib.parse.urlencode([(k, isinstance(v, str) and v.encode('utf-8') or v) for k, v in params]) @@ -1100,7 +1099,7 @@ class Facebook(object): return self # __init__ hard-codes into en_US - if args is not None and not args.has_key('locale'): + if args is not None and 'locale' not in args: args['locale'] = self.locale # @author: houyr @@ -1108,8 +1107,8 @@ class Facebook(object): post_data = self.unicode_urlencode(self._build_post_args(method, args)) if self.proxy: - proxy_handler = urllib2.ProxyHandler(self.proxy) - opener = urllib2.build_opener(proxy_handler) + proxy_handler = urllib.request.ProxyHandler(self.proxy) + opener = urllib.request.build_opener(proxy_handler) if secure: response = opener.open(self.facebook_secure_url, post_data).read() else: @@ -1130,7 +1129,7 @@ class Facebook(object): Named arguments are passed as GET query string parameters. """ - return 'http://www.facebook.com/%s.php?%s' % (page, urllib.urlencode(args)) + return 'http://www.facebook.com/%s.php?%s' % (page, urllib.parse.urlencode(args)) def get_app_url(self, path=''): @@ -1255,7 +1254,7 @@ class Facebook(object): try: self.auth.getSession() - except FacebookError, e: + except FacebookError as e: self.auth_token = None return False @@ -1349,7 +1348,7 @@ class Facebook(object): if timeout and '%s_time' % prefix in post and time.time() - float(post['%s_time' % prefix]) > timeout: return None - args = dict([(key[len(prefix + '_'):], value) for key, value in args.items() if key.startswith(prefix)]) + args = dict([(key[len(prefix + '_'):], value) for key, value in list(args.items()) if key.startswith(prefix)]) hash = self._hash_args(args) @@ -1406,24 +1405,24 @@ if __name__ == '__main__': facebook.login() # Login to the window, then press enter - print 'After logging in, press enter...' - raw_input() + print('After logging in, press enter...') + input() facebook.auth.getSession() - print 'Session Key: ', facebook.session_key - print 'Your UID: ', facebook.uid + print('Session Key: ', facebook.session_key) + print('Your UID: ', facebook.uid) info = facebook.users.getInfo([facebook.uid], ['name', 'birthday', 'affiliations', 'sex'])[0] - print 'Your Name: ', info['name'] - print 'Your Birthday: ', info['birthday'] - print 'Your Gender: ', info['sex'] + print('Your Name: ', info['name']) + print('Your Birthday: ', info['birthday']) + print('Your Gender: ', info['sex']) friends = facebook.friends.get() friends = facebook.users.getInfo(friends[0:5], ['name', 'birthday', 'relationship_status']) for friend in friends: - print friend['name'], 'has a birthday on', friend['birthday'], 'and is', friend['relationship_status'] + print(friend['name'], 'has a birthday on', friend['birthday'], 'and is', friend['relationship_status']) arefriends = facebook.friends.areFriends([friends[0]['uid']], [friends[1]['uid']]) diff --git a/helios_auth/auth_systems/facebookclient/djangofb/__init__.py b/helios_auth/auth_systems/facebookclient/djangofb/__init__.py index 68b1b27c37d19e1372369837006947b4f60ef7c5..48f720be150401e5a063e7508aada478951fd514 100644 --- a/helios_auth/auth_systems/facebookclient/djangofb/__init__.py +++ b/helios_auth/auth_systems/facebookclient/djangofb/__init__.py @@ -64,7 +64,7 @@ def require_login(next=None, internal=None): """ def decorator(view): def newview(request, *args, **kwargs): - next = newview.next + next = newview.__next__ internal = newview.internal try: @@ -127,7 +127,7 @@ def require_add(next=None, internal=None, on_install=None): """ def decorator(view): def newview(request, *args, **kwargs): - next = newview.next + next = newview.__next__ internal = newview.internal try: diff --git a/helios_auth/auth_systems/facebookclient/djangofb/context_processors.py b/helios_auth/auth_systems/facebookclient/djangofb/context_processors.py index 6f954397308f7af525d9fa600fb29e8cf6902c33..82805a537f3033eb9f7b7597cc2ca7b17fddb82e 100644 --- a/helios_auth/auth_systems/facebookclient/djangofb/context_processors.py +++ b/helios_auth/auth_systems/facebookclient/djangofb/context_processors.py @@ -1,6 +1,6 @@ def messages(request): """Returns messages similar to ``django.core.context_processors.auth``.""" if hasattr(request, 'facebook') and request.facebook.uid is not None: - from models import Message + from .models import Message messages = Message.objects.get_and_delete_all(uid=request.facebook.uid) return {'messages': messages} \ No newline at end of file diff --git a/helios_auth/auth_systems/facebookclient/djangofb/default_app/urls.py b/helios_auth/auth_systems/facebookclient/djangofb/default_app/urls.py index f75d8d258360fc43e12b2343182d89f14a01ef8c..d6084742bb17200bb3a452905e94f734f4c5b6be 100644 --- a/helios_auth/auth_systems/facebookclient/djangofb/default_app/urls.py +++ b/helios_auth/auth_systems/facebookclient/djangofb/default_app/urls.py @@ -1,6 +1,6 @@ from django.conf.urls import url -from views import canvas +from .views import canvas urlpatterns = [ url(r'^$', canvas), diff --git a/helios_auth/auth_systems/facebookclient/djangofb/default_app/views.py b/helios_auth/auth_systems/facebookclient/djangofb/default_app/views.py index 609314fe01b3bf546984841b9dded39756bfa0cb..1cd91f831ca38cbd27f6823e9f979bd83d5df729 100644 --- a/helios_auth/auth_systems/facebookclient/djangofb/default_app/views.py +++ b/helios_auth/auth_systems/facebookclient/djangofb/default_app/views.py @@ -9,7 +9,7 @@ from django.http import HttpResponse import facebook.djangofb as facebook # The User model defined in models.py -from models import User +from .models import User # We'll require login for our canvas page. This # isn't necessarily a good idea, as we might want diff --git a/helios_auth/auth_systems/facebookclient/djangofb/models.py b/helios_auth/auth_systems/facebookclient/djangofb/models.py index b5d2c62221e9926f7ab4b57cb95fb71ab22be2da..07604f40d2525e1837c099442af2de9f8479e1ae 100644 --- a/helios_auth/auth_systems/facebookclient/djangofb/models.py +++ b/helios_auth/auth_systems/facebookclient/djangofb/models.py @@ -30,7 +30,7 @@ class Message(models.Model): return self.get_status_display().lower() def as_fbml(self): - return mark_safe(u'<fb:%s message="%s" />' % ( + return mark_safe('<fb:%s message="%s" />' % ( self._fb_tag(), escape(self.message), )) diff --git a/helios_auth/auth_systems/google.py b/helios_auth/auth_systems/google.py index 03419915600fe58636ce9e2098a0219075fa804d..b713404de7d1bb9b527294f0c2baa571679c36f3 100644 --- a/helios_auth/auth_systems/google.py +++ b/helios_auth/auth_systems/google.py @@ -4,11 +4,12 @@ Google Authentication """ import httplib2 -import json from django.conf import settings from django.core.mail import send_mail from oauth2client.client import OAuth2WebServerFlow +from helios_auth import utils + # some parameters to indicate that status updating is not possible STATUS_UPDATES = False @@ -30,7 +31,7 @@ def get_auth_url(request, redirect_url): def get_user_info_after_auth(request): flow = get_flow(request.session['google-redirect-url']) - if not request.GET.has_key('code'): + if 'code' not in request.GET: return None code = request.GET['code'] @@ -48,7 +49,7 @@ def get_user_info_after_auth(request): http = credentials.authorize(http) (resp_headers, content) = http.request("https://people.googleapis.com/v1/people/me?personFields=names", "GET") - response = json.loads(content) + response = utils.from_json(content) name = response['names'][0]['displayName'] diff --git a/helios_auth/auth_systems/linkedin.py b/helios_auth/auth_systems/linkedin.py index 696eda9836f0c141360d24f00739d8ed66fca95e..e75e0786d46a508f9846bd6b9d1491be1bdbcbc5 100644 --- a/helios_auth/auth_systems/linkedin.py +++ b/helios_auth/auth_systems/linkedin.py @@ -2,18 +2,12 @@ LinkedIn Authentication """ -from oauthclient import client - -from django.urls import reverse -from django.http import HttpResponseRedirect - -from helios_auth import utils - from xml.etree import ElementTree -import logging - from django.conf import settings + +from .oauthclient import client + API_KEY = settings.LINKEDIN_API_KEY API_SECRET = settings.LINKEDIN_API_SECRET diff --git a/helios_auth/auth_systems/live.py b/helios_auth/auth_systems/live.py index 9f34a2783198003f09a74abb1671d9630cf3895a..348d02ff34a22d685b3d6dbe46aa5885ad162b4d 100644 --- a/helios_auth/auth_systems/live.py +++ b/helios_auth/auth_systems/live.py @@ -5,14 +5,14 @@ so much like Facebook # NOT WORKING YET because Windows Live documentation and status is unclear. Is it in beta? I think it is. """ -import logging +import urllib.parse +import urllib.request from django.conf import settings + APP_ID = settings.LIVE_APP_ID APP_SECRET = settings.LIVE_APP_SECRET -import urllib, urllib2, cgi - # some parameters to indicate that status updating is possible STATUS_UPDATES = False # STATUS_UPDATE_WORDING_TEMPLATE = "Send %s to your facebook status" @@ -21,17 +21,17 @@ from helios_auth import utils def live_url(url, params): if params: - return "https://graph.facebook.com%s?%s" % (url, urllib.urlencode(params)) + return "https://graph.facebook.com%s?%s" % (url, urllib.parse.urlencode(params)) else: return "https://graph.facebook.com%s" % url def live_get(url, params): full_url = live_url(url,params) - return urllib2.urlopen(full_url).read() + return urllib.request.urlopen(full_url).read() def live_post(url, params): full_url = live_url(url, None) - return urllib2.urlopen(full_url, urllib.urlencode(params)).read() + return urllib.request.urlopen(full_url, urllib.parse.urlencode(params)).read() def get_auth_url(request, redirect_url): request.session['live_redirect_uri'] = redirect_url @@ -41,16 +41,16 @@ def get_auth_url(request, redirect_url): 'scope': 'publish_stream'}) def get_user_info_after_auth(request): - args = facebook_get('/oauth/access_token', { + args = live_get('/oauth/access_token', { 'client_id' : APP_ID, 'redirect_uri' : request.session['fb_redirect_uri'], - 'client_secret' : API_SECRET, + 'client_secret' : APP_SECRET, 'code' : request.GET['code'] }) - access_token = cgi.parse_qs(args)['access_token'][0] + access_token = urllib.parse.parse_qs(args)['access_token'][0] - info = utils.from_json(facebook_get('/me', {'access_token':access_token})) + info = utils.from_json(live_get('/me', {'access_token':access_token})) return {'type': 'facebook', 'user_id' : info['id'], 'name': info['name'], 'info': info, 'token': {'access_token': access_token}} @@ -58,7 +58,7 @@ def update_status(user_id, user_info, token, message): """ post a message to the auth system's update stream, e.g. twitter stream """ - result = facebook_post('/me/feed', { + result = live_post('/me/feed', { 'access_token': token['access_token'], 'message': message }) diff --git a/helios_auth/auth_systems/oauthclient/client.py b/helios_auth/auth_systems/oauthclient/client.py index c9e6b829bb946694130cabcc6b336939864bcb3a..4980535e45ab58942cc632c00ecc50c0dd33d578 100644 --- a/helios_auth/auth_systems/oauthclient/client.py +++ b/helios_auth/auth_systems/oauthclient/client.py @@ -7,12 +7,11 @@ Used the SampleClient from the OAUTH.org example python client as basis. props to leahculver for making a very hard to use but in the end usable oauth lib. ''' -import httplib -import urllib, urllib2 -import time +import urllib.request import webbrowser -import oauth as oauth -from urlparse import urlparse + +from . import oauth as oauth + class LoginOAuthClient(oauth.OAuthClient): @@ -36,31 +35,35 @@ class LoginOAuthClient(oauth.OAuthClient): self.sha1_method = oauth.OAuthSignatureMethod_HMAC_SHA1() self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret) - if ((oauth_token != None) and (oauth_token_secret!=None)): + if (oauth_token is not None) and (oauth_token_secret is not None): self.token = oauth.OAuthConsumer(oauth_token, oauth_token_secret) else: self.token = None - def oauth_request(self,url, args = {}, method=None): - if (method==None): - if args=={}: + def oauth_request(self, url, args=None, method=None): + if args is None: + args = {} + if method is None: + if args == {}: method = "GET" else: method = "POST" req = oauth.OAuthRequest.from_consumer_and_token(self.consumer, self.token, method, url, args) req.sign_request(self.sha1_method, self.consumer,self.token) - if (method=="GET"): + if method== "GET": return self.http_wrapper(req.to_url()) - elif (method == "POST"): + elif method == "POST": return self.http_wrapper(req.get_normalized_http_url(),req.to_postdata()) #this is barely working. (i think. mostly it is that everyone else is using httplib) - def http_wrapper(self, url, postdata={}): + def http_wrapper(self, url, postdata=None): + if postdata is None: + postdata = {} try: - if (postdata != {}): - f = urllib.urlopen(url, postdata) + if postdata != {}: + f = urllib.request.urlopen(url, postdata) else: - f = urllib.urlopen(url) + f = urllib.request.urlopen(url) response = f.read() except: import traceback @@ -133,26 +136,26 @@ if __name__ == '__main__': consumer_key = '' consumer_secret = '' while not consumer_key: - consumer_key = raw_input('Please enter consumer key: ') + consumer_key = input('Please enter consumer key: ') while not consumer_secret: - consumer_secret = raw_input('Please enter consumer secret: ') + consumer_secret = input('Please enter consumer secret: ') auth_client = LoginOAuthClient(consumer_key,consumer_secret) tok = auth_client.get_request_token() token = tok['oauth_token'] token_secret = tok['oauth_token_secret'] url = auth_client.get_authorize_url(token) webbrowser.open(url) - print "Visit this URL to authorize your app: " + url - response_token = raw_input('What is the oauth_token from twitter: ') + print("Visit this URL to authorize your app: " + url) + response_token = input('What is the oauth_token from twitter: ') response_client = LoginOAuthClient(consumer_key, consumer_secret,token, token_secret, server_params={}) tok = response_client.get_access_token() - print "Making signed request" + print("Making signed request") #verify user access content = response_client.oauth_request('https://twitter.com/account/verify_credentials.json', method='POST') #make an update #content = response_client.oauth_request('https://twitter.com/statuses/update.xml', {'status':'Updated from a python oauth client. awesome.'}, method='POST') - print content + print(content) - print 'Done.' + print('Done.') diff --git a/helios_auth/auth_systems/oauthclient/oauth/__init__.py b/helios_auth/auth_systems/oauthclient/oauth/__init__.py index baf543ed4a8db09d92ee91b925d18bc6f5d09f93..f318a8af4421f92c9a5276d619fd8ef52dbe9644 100755 --- a/helios_auth/auth_systems/oauthclient/oauth/__init__.py +++ b/helios_auth/auth_systems/oauthclient/oauth/__init__.py @@ -1,10 +1,8 @@ -import cgi -import urllib -import time -import random -import urlparse -import hmac import binascii +import hmac +import random +import time +import urllib.parse VERSION = '1.0' # Hi Blaine! HTTP_METHOD = 'GET' @@ -22,7 +20,7 @@ def build_authenticate_header(realm=''): # url escape def escape(s): # escape '/' too - return urllib.quote(s, safe='~') + return urllib.parse.quote(s, safe='~') # util function: current timestamp # seconds since epoch (UTC) @@ -60,12 +58,12 @@ class OAuthToken(object): self.secret = secret def to_string(self): - return urllib.urlencode({'oauth_token': self.key, 'oauth_token_secret': self.secret}) + return urllib.parse.urlencode({'oauth_token': self.key, 'oauth_token_secret': self.secret}) # return a token from something like: # oauth_token_secret=digg&oauth_token=digg def from_string(s): - params = cgi.parse_qs(s, keep_blank_values=False) + params = urllib.parse.parse_qs(s, keep_blank_values=False) key = params['oauth_token'][0] secret = params['oauth_token_secret'][0] return OAuthToken(key, secret) @@ -112,7 +110,7 @@ class OAuthRequest(object): # get any non-oauth parameters def get_nonoauth_parameters(self): parameters = {} - for k, v in self.parameters.iteritems(): + for k, v in self.parameters.items(): # ignore oauth parameters if k.find('oauth_') < 0: parameters[k] = v @@ -123,14 +121,14 @@ class OAuthRequest(object): auth_header = 'OAuth realm="%s"' % realm # add the oauth parameters if self.parameters: - for k, v in self.parameters.iteritems(): + for k, v in self.parameters.items(): if k[:6] == 'oauth_': auth_header += ', %s="%s"' % (k, escape(str(v))) return {'Authorization': auth_header} # serialize as post data for a POST request def to_postdata(self): - return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) for k, v in self.parameters.iteritems()]) + return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) for k, v in self.parameters.items()]) # serialize as a url for a GET request def to_url(self): @@ -144,7 +142,7 @@ class OAuthRequest(object): del params['oauth_signature'] except: pass - key_values = params.items() + key_values = list(params.items()) # sort lexicographically, first after key, then after value key_values.sort() # combine key value pairs in string and escape @@ -156,7 +154,7 @@ class OAuthRequest(object): # parses the url and rebuilds it to be scheme://host/path def get_normalized_http_url(self): - parts = urlparse.urlparse(self.http_url) + parts = urllib.parse.urlparse(self.http_url) url_string = '%s://%s%s' % (parts[0], parts[1], parts[2]) # scheme, netloc, path return url_string @@ -194,7 +192,7 @@ class OAuthRequest(object): parameters.update(query_params) # URL parameters - param_str = urlparse.urlparse(http_url)[4] # query + param_str = urllib.parse.urlparse(http_url)[4] # query url_params = OAuthRequest._split_url_string(param_str) parameters.update(url_params) @@ -249,15 +247,15 @@ class OAuthRequest(object): # split key-value param_parts = param.split('=', 1) # remove quotes and unescape the value - params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) + params[param_parts[0]] = urllib.parse.unquote(param_parts[1].strip('\"')) return params _split_header = staticmethod(_split_header) # util function: turn url string into parameters, has to do some unescaping def _split_url_string(param_str): - parameters = cgi.parse_qs(param_str, keep_blank_values=False) - for k, v in parameters.iteritems(): - parameters[k] = urllib.unquote(v[0]) + parameters = urllib.parse.parse_qs(param_str, keep_blank_values=False) + for k, v in parameters.items(): + parameters[k] = urllib.parse.unquote(v[0]) return parameters _split_url_string = staticmethod(_split_url_string) @@ -273,7 +271,7 @@ class OAuthServer(object): self.signature_methods = signature_methods or {} def set_data_store(self, oauth_data_store): - self.data_store = data_store + self.data_store = oauth_data_store def get_data_store(self): return self.data_store @@ -351,7 +349,7 @@ class OAuthServer(object): # get the signature method object signature_method = self.signature_methods[signature_method] except: - signature_method_names = ', '.join(self.signature_methods.keys()) + signature_method_names = ', '.join(list(self.signature_methods.keys())) raise OAuthError('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names)) return signature_method @@ -499,11 +497,11 @@ class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod): # hmac object try: - import hashlib # 2.5 - hashed = hmac.new(key, raw, hashlib.sha1) + from Crypto.Hash import SHA1 + hashed = hmac.new(key, raw, SHA1) except: - import sha # deprecated - hashed = hmac.new(key, raw, sha) + import hashlib + hashed = hmac.new(key, raw, hashlib.sha1) # calculate the digest base 64 return binascii.b2a_base64(hashed.digest())[:-1] diff --git a/helios_auth/auth_systems/openid/util.py b/helios_auth/auth_systems/openid/util.py index 1ed33f3be0f82de0f2f9d545a79ccf8b972bd878..2a2e12a429b0206f6700acbb929b5e01ffdfe11f 100644 --- a/helios_auth/auth_systems/openid/util.py +++ b/helios_auth/auth_systems/openid/util.py @@ -3,20 +3,15 @@ Utility code for the Django example consumer and server. """ -from urlparse import urljoin +from urllib.parse import urljoin -from django.db import connection -from django.template.context import RequestContext -from django.template import loader -from django import http +from django.conf import settings from django.core.exceptions import ImproperlyConfigured +from django.db import connection from django.urls import reverse as reverseURL - -from django.conf import settings - -from openid.store.filestore import FileOpenIDStore from openid.store import sqlstore -from openid.yadis.constants import YADIS_CONTENT_TYPE +from openid.store.filestore import FileOpenIDStore + def getOpenIDStore(filestore_path, table_prefix): """ @@ -69,14 +64,13 @@ def getOpenIDStore(filestore_path, table_prefix): s = types[db_engine](connection.connection, **tablenames) except KeyError: - raise ImproperlyConfigured, \ - "Database engine %s not supported by OpenID library" % \ - (db_engine,) + raise ImproperlyConfigured("Database engine %s not supported by OpenID library" % \ + (db_engine,)) try: s.createTables() - except (SystemExit, KeyboardInterrupt, MemoryError), e: - raise + except (SystemExit, KeyboardInterrupt, MemoryError) as e: + raise e except: # XXX This is not the Right Way to do this, but because the # underlying database implementation might differ in behavior @@ -138,5 +132,5 @@ def normalDict(request_data): values are lists, because in OpenID, each key in the query arg set can have at most one value. """ - return dict((k, v[0]) for k, v in request_data.iteritems()) + return dict((k, v[0]) for k, v in request_data.items()) diff --git a/helios_auth/auth_systems/openid/view_helpers.py b/helios_auth/auth_systems/openid/view_helpers.py index 06eef8a287bbbf0d5a05d32d8ed0e13939761eac..de79b451a037800f974e33535e52569af754c1c6 100644 --- a/helios_auth/auth_systems/openid/view_helpers.py +++ b/helios_auth/auth_systems/openid/view_helpers.py @@ -1,14 +1,8 @@ - -from django import http -from django.http import HttpResponseRedirect - from openid.consumer import consumer from openid.consumer.discover import DiscoveryFailure from openid.extensions import ax, pape, sreg -from openid.yadis.constants import YADIS_HEADER_NAME, YADIS_CONTENT_TYPE -from openid.server.trustroot import RP_RETURN_TO_URL_TYPE -import util +from . import util PAPE_POLICIES = [ 'AUTH_PHISHING_RESISTANT', @@ -56,16 +50,12 @@ def start_openid(session, openid_url, trust_root, return_to): # Start OpenID authentication. c = get_consumer(session) - error = None try: auth_request = c.begin(openid_url) - except DiscoveryFailure, e: + except DiscoveryFailure as e: # Some other protocol-level failure occurred. - error = "OpenID discovery error: %s" % (str(e),) - - if error: - raise Exception("error in openid") + raise Exception("error in openid: OpenID discovery error") from e # Add Simple Registration request information. Some fields # are optional, some are required. It's possible that the @@ -80,7 +70,7 @@ def start_openid(session, openid_url, trust_root, return_to): # XXX - uses myOpenID-compatible schema values, which are # not those listed at axschema.org. - for k, v in AX_REQUIRED_FIELDS.iteritems(): + for k, v in AX_REQUIRED_FIELDS.items(): ax_request.add(ax.AttrInfo(v, required=True)) auth_request.addExtension(ax_request) @@ -123,12 +113,12 @@ def finish_openid(session, request_args, return_to): ax_response = ax.FetchResponse.fromSuccessResponse(response) if ax_response: - for k, v in AX_REQUIRED_FIELDS.iteritems(): + for k, v in AX_REQUIRED_FIELDS.items(): """ the values are the URIs, they are the key into the data the key is the shortname """ - if ax_response.data.has_key(v): + if v in ax_response.data: ax_items[k] = ax_response.get(v) # Map different consumer status codes to template contexts. @@ -141,7 +131,7 @@ def finish_openid(session, request_args, return_to): consumer.SUCCESS: {'url': response.getDisplayIdentifier(), - 'sreg': sreg_response and sreg_response.items(), + 'sreg': sreg_response and list(sreg_response.items()), 'ax': ax_items} } diff --git a/helios_auth/auth_systems/password.py b/helios_auth/auth_systems/password.py index aadb03fb05e52d9bbe4e9b2d5e8e02933b89590f..36f42ecffcc109f283d157e1ffe2a37fadb8b706 100644 --- a/helios_auth/auth_systems/password.py +++ b/helios_auth/auth_systems/password.py @@ -52,7 +52,7 @@ def password_login_view(request): # set this in case we came here straight from the multi-login chooser # and thus did not have a chance to hit the "start/password" URL request.session['auth_system_name'] = 'password' - if request.POST.has_key('return_url'): + if 'return_url' in request.POST: request.session['auth_return_url'] = request.POST.get('return_url') if form.is_valid(): diff --git a/helios_auth/auth_systems/twitter.py b/helios_auth/auth_systems/twitter.py index 8739d607ebcb1d85b0b539c8f85eb34cceaa4ce8..541ac4df7cd874259a71be3566246fa3b2a48b57 100644 --- a/helios_auth/auth_systems/twitter.py +++ b/helios_auth/auth_systems/twitter.py @@ -2,7 +2,7 @@ Twitter Authentication """ -from oauthclient import client +from .oauthclient import client from django.conf.urls import url from django.urls import reverse diff --git a/helios_auth/auth_systems/yahoo.py b/helios_auth/auth_systems/yahoo.py index 5131a19be520301ab4ea6643e88d97015d186d0f..ccfdc12ffd8850dc17feb54bee08b4fa784a5c1e 100644 --- a/helios_auth/auth_systems/yahoo.py +++ b/helios_auth/auth_systems/yahoo.py @@ -6,7 +6,7 @@ Yahoo Authentication from django.conf import settings from django.core.mail import send_mail -from openid import view_helpers +from .openid import view_helpers # some parameters to indicate that status updating is not possible STATUS_UPDATES = False diff --git a/helios_auth/jsonfield.py b/helios_auth/jsonfield.py index 34cecf7894487c1141388abe13d5a0934a4ab9dc..ab66d5d5d95f5e48860e36f88a4f70124cfee558 100644 --- a/helios_auth/jsonfield.py +++ b/helios_auth/jsonfield.py @@ -5,10 +5,12 @@ http://www.djangosnippets.org/snippets/377/ """ import json -from django.core.exceptions import ValidationError + from django.core.serializers.json import DjangoJSONEncoder from django.db import models +from . import utils + class JSONField(models.TextField): """ @@ -37,14 +39,10 @@ class JSONField(models.TextField): # noinspection PyUnusedLocal def from_db_value(self, value, *args, **kwargs): - if value == "" or value is None: + parsed_value = utils.from_json(value) + if parsed_value is None: return None - try: - parsed_value = json.loads(value) - except Exception as e: - raise ValidationError("Received value is not JSON", e) - if self.json_type and parsed_value: parsed_value = self.json_type.fromJSONDict(parsed_value, **self.deserialization_params) @@ -55,7 +53,7 @@ class JSONField(models.TextField): 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 is None: diff --git a/helios_auth/migrations/0001_initial.py b/helios_auth/migrations/0001_initial.py index fc54ff2aa58d29872fdd0453e87ffb9123974af8..002068a63be3355f5eb285660f1b0eb5b7514816 100644 --- a/helios_auth/migrations/0001_initial.py +++ b/helios_auth/migrations/0001_initial.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals from django.db import models, migrations + import helios_auth.jsonfield diff --git a/helios_auth/models.py b/helios_auth/models.py index fb050d225808599e95459e057f7ff0489e5c1ecf..d4e3a3282ce220688b109da22fa018c667817125 100644 --- a/helios_auth/models.py +++ b/helios_auth/models.py @@ -8,8 +8,8 @@ Ben Adida """ from django.db import models -from auth_systems import AUTH_SYSTEMS -from jsonfield import JSONField +from .auth_systems import AUTH_SYSTEMS +from .jsonfield import JSONField # an exception to catch when a user is no longer authenticated @@ -53,7 +53,7 @@ class User(models.Model): if not created_p: # special case the password: don't replace it if it exists - if obj.info.has_key('password'): + if 'password' in obj.info: info['password'] = obj.info['password'] obj.info = info @@ -64,7 +64,7 @@ class User(models.Model): return obj def can_update_status(self): - if not AUTH_SYSTEMS.has_key(self.user_type): + if self.user_type not in AUTH_SYSTEMS: return False return AUTH_SYSTEMS[self.user_type].STATUS_UPDATES @@ -74,7 +74,7 @@ class User(models.Model): Certain auth systems can choose to limit election creation to certain users. """ - if not AUTH_SYSTEMS.has_key(self.user_type): + if self.user_type not in AUTH_SYSTEMS: return False return AUTH_SYSTEMS[self.user_type].can_create_election(self.user_id, self.info) @@ -86,16 +86,16 @@ class User(models.Model): return AUTH_SYSTEMS[self.user_type].STATUS_UPDATE_WORDING_TEMPLATE def update_status(self, status): - if AUTH_SYSTEMS.has_key(self.user_type): + if self.user_type in AUTH_SYSTEMS: AUTH_SYSTEMS[self.user_type].update_status(self.user_id, self.info, self.token, status) def send_message(self, subject, body): - if AUTH_SYSTEMS.has_key(self.user_type): + if self.user_type in AUTH_SYSTEMS: subject = subject.split("\n")[0] AUTH_SYSTEMS[self.user_type].send_message(self.user_id, self.name, self.info, subject, body) def send_notification(self, message): - if AUTH_SYSTEMS.has_key(self.user_type): + if self.user_type in AUTH_SYSTEMS: if hasattr(AUTH_SYSTEMS[self.user_type], 'send_notification'): AUTH_SYSTEMS[self.user_type].send_notification(self.user_id, self.info, message) @@ -110,7 +110,7 @@ class User(models.Model): return False # no constraint? Then eligible! - if not eligibility_case.has_key('constraint'): + if 'constraint' not in eligibility_case: return True # from here on we know we match the auth system, but do we match one of the constraints? @@ -141,14 +141,14 @@ class User(models.Model): if self.name: return self.name - if self.info.has_key('name'): + if 'name' in self.info: return self.info['name'] return self.user_id @property def public_url(self): - if AUTH_SYSTEMS.has_key(self.user_type): + if self.user_type in AUTH_SYSTEMS: if hasattr(AUTH_SYSTEMS[self.user_type], 'public_url'): return AUTH_SYSTEMS[self.user_type].public_url(self.user_id) diff --git a/helios_auth/security/__init__.py b/helios_auth/security/__init__.py index d3c5ac184a160d6790b1f04e8584341919dd3ef5..1fa30e1cf3d5edef11b7a445f4527235f0f567d8 100644 --- a/helios_auth/security/__init__.py +++ b/helios_auth/security/__init__.py @@ -12,7 +12,7 @@ from django.http import HttpResponseRedirect # nicely update the wrapper function from functools import update_wrapper -import oauth +from . import oauth from helios_auth.models import User FIELDS_TO_SAVE = 'FIELDS_TO_SAVE' @@ -93,10 +93,10 @@ def get_user(request): # request.session.set_expiry(settings.SESSION_COOKIE_AGE) # set up CSRF protection if needed - if not request.session.has_key('csrf_token') or (type(request.session['csrf_token']) != str and type(request.session['csrf_token']) != unicode): + if 'csrf_token' not in request.session or not isinstance(request.session['csrf_token'], str): request.session['csrf_token'] = str(uuid.uuid4()) - if request.session.has_key('user'): + if 'user' in request.session: user = request.session['user'] # find the user @@ -109,7 +109,7 @@ def check_csrf(request): if request.method != "POST": return HttpResponseNotAllowed("only a POST for this URL") - if (not request.POST.has_key('csrf_token')) or (request.POST['csrf_token'] != request.session['csrf_token']): + if ('csrf_token' not in request.POST) or (request.POST['csrf_token'] != request.session['csrf_token']): raise Exception("A CSRF problem was detected") def save_in_session_across_logouts(request, field_name, field_value): diff --git a/helios_auth/security/oauth.py b/helios_auth/security/oauth.py index 71676c89f039eebadbb1129081e8e96d2f638ee7..568272bd835eaae3a3f9238b821fab8b870222e8 100644 --- a/helios_auth/security/oauth.py +++ b/helios_auth/security/oauth.py @@ -6,14 +6,12 @@ Hacked a bit by Ben Adida (ben@adida.net) so that: - access tokens are looked up with an extra param of consumer """ -import urllib -import time -import random -import urlparse -import hmac import base64 +import hmac import logging -import hashlib +import random +import time +import urllib.parse VERSION = '1.0' # Hi Blaine! HTTP_METHOD = 'GET' @@ -31,7 +29,7 @@ def build_authenticate_header(realm=''): # url escape def escape(s): # escape '/' too - return urllib.quote(s, safe='~') + return urllib.parse.quote(s, safe='~') # util function: current timestamp # seconds since epoch (UTC) @@ -69,13 +67,13 @@ class OAuthToken(object): self.secret = secret def to_string(self): - return urllib.urlencode({'oauth_token': self.key, 'oauth_token_secret': self.secret}) + return urllib.parse.urlencode({'oauth_token': self.key, 'oauth_token_secret': self.secret}) # return a token from something like: # oauth_token_secret=digg&oauth_token=digg @staticmethod def from_string(s): - params = urlparse.parse_qs(s, keep_blank_values=False) + params = urllib.parse.parse_qs(s, keep_blank_values=False) key = params['oauth_token'][0] secret = params['oauth_token_secret'][0] return OAuthToken(key, secret) @@ -124,7 +122,7 @@ class OAuthRequest(object): # get any non-oauth parameters def get_nonoauth_parameters(self): parameters = {} - for k, v in self.parameters.iteritems(): + for k, v in self.parameters.items(): # ignore oauth parameters if k.find('oauth_') < 0: parameters[k] = v @@ -135,7 +133,7 @@ class OAuthRequest(object): auth_header = 'OAuth realm="%s"' % realm # add the oauth parameters if self.parameters: - for k, v in self.parameters.iteritems(): + for k, v in self.parameters.items(): # only if it's a standard OAUTH param (Ben) if k in self.OAUTH_PARAMS: auth_header += ', %s="%s"' % (k, escape(str(v))) @@ -143,7 +141,7 @@ class OAuthRequest(object): # serialize as post data for a POST request def to_postdata(self): - return '&'.join('%s=%s' % (escape(str(k)), escape(str(v))) for k, v in self.parameters.iteritems()) + return '&'.join('%s=%s' % (escape(str(k)), escape(str(v))) for k, v in self.parameters.items()) # serialize as a url for a GET request def to_url(self): @@ -157,7 +155,7 @@ class OAuthRequest(object): del params['oauth_signature'] except: pass - key_values = params.items() + key_values = list(params.items()) # sort lexicographically, first after key, then after value key_values.sort() # combine key value pairs in string and escape @@ -169,7 +167,7 @@ class OAuthRequest(object): # parses the url and rebuilds it to be scheme://host/path def get_normalized_http_url(self): - parts = urlparse.urlparse(self.http_url) + parts = urllib.parse.urlparse(self.http_url) url_string = '%s://%s%s' % (parts[0], parts[1], parts[2]) # scheme, netloc, path return url_string @@ -208,7 +206,7 @@ class OAuthRequest(object): parameters.update(query_params) # URL parameters - param_str = urlparse.urlparse(http_url)[4] # query + param_str = urllib.parse.urlparse(http_url)[4] # query url_params = OAuthRequest._split_url_string(param_str) parameters.update(url_params) @@ -263,15 +261,15 @@ class OAuthRequest(object): # split key-value param_parts = param.split('=', 1) # remove quotes and unescape the value - params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) + params[param_parts[0]] = urllib.parse.unquote(param_parts[1].strip('\"')) return params # util function: turn url string into parameters, has to do some unescaping @staticmethod def _split_url_string(param_str): - parameters = urlparse.parse_qs(param_str, keep_blank_values=False) - for k, v in parameters.iteritems(): - parameters[k] = urllib.unquote(v[0]) + parameters = urllib.parse.parse_qs(param_str, keep_blank_values=False) + for k, v in parameters.items(): + parameters[k] = urllib.parse.unquote(v[0]) return parameters # OAuthServer is a worker to check a requests validity against a data store @@ -364,7 +362,7 @@ class OAuthServer(object): # get the signature method object signature_method = self.signature_methods[signature_method] except: - signature_method_names = ', '.join(self.signature_methods.keys()) + signature_method_names = ', '.join(list(self.signature_methods.keys())) raise OAuthError('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names)) return signature_method @@ -513,7 +511,12 @@ class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod): key, raw = self.build_signature_base_string(oauth_request, consumer, token) # hmac object - hashed = hmac.new(key, raw, hashlib.sha1) + try: + from Crypto.Hash import SHA1 + hashed = hmac.new(key, raw, SHA1) + except: + import hashlib + hashed = hmac.new(key, raw, hashlib.sha1) # calculate the digest base 64 return base64.b64encode(hashed.digest()) diff --git a/helios_auth/tests.py b/helios_auth/tests.py index de21e3b09c600084194a47bd5cd0f73522a74daa..934933258f49fb8a64752fc241340a1ad4489789 100644 --- a/helios_auth/tests.py +++ b/helios_auth/tests.py @@ -3,16 +3,15 @@ Unit Tests for Auth Systems """ import unittest -import models +from django.core import mail from django.db import IntegrityError, transaction - -from django.test.client import Client from django.test import TestCase +from django.urls import reverse -from django.core import mail +from . import models, views +from .auth_systems import AUTH_SYSTEMS, password as password_views -from auth_systems import AUTH_SYSTEMS class UserModelTests(unittest.TestCase): @@ -23,7 +22,7 @@ class UserModelTests(unittest.TestCase): """ there should not be two users with the same user_type and user_id """ - for auth_system, auth_system_module in AUTH_SYSTEMS.iteritems(): + for auth_system, auth_system_module in AUTH_SYSTEMS.items(): models.User.objects.create(user_type = auth_system, user_id = 'foobar', info={'name':'Foo Bar'}) def double_insert(): @@ -36,22 +35,22 @@ class UserModelTests(unittest.TestCase): """ shouldn't create two users, and should reset the password """ - for auth_system, auth_system_module in AUTH_SYSTEMS.iteritems(): + for auth_system, auth_system_module in AUTH_SYSTEMS.items(): u = models.User.update_or_create(user_type = auth_system, user_id = 'foobar_cou', info={'name':'Foo Bar'}) def double_update_or_create(): new_name = 'Foo2 Bar' u2 = models.User.update_or_create(user_type = auth_system, user_id = 'foobar_cou', info={'name': new_name}) - self.assertEquals(u.id, u2.id) - self.assertEquals(u2.info['name'], new_name) + self.assertEqual(u.id, u2.id) + self.assertEqual(u2.info['name'], new_name) def test_can_create_election(self): """ check that auth systems have the can_create_election call and that it's true for the common ones """ - for auth_system, auth_system_module in AUTH_SYSTEMS.iteritems(): + for auth_system, auth_system_module in AUTH_SYSTEMS.items(): assert(hasattr(auth_system_module, 'can_create_election')) if auth_system != 'clever': assert(auth_system_module.can_create_election('foobar', {})) @@ -62,13 +61,13 @@ class UserModelTests(unittest.TestCase): check that a user set up with status update ability reports it as such, and otherwise does not report it """ - for auth_system, auth_system_module in AUTH_SYSTEMS.iteritems(): + for auth_system, auth_system_module in AUTH_SYSTEMS.items(): u = models.User.update_or_create(user_type = auth_system, user_id = 'foobar_status_update', info={'name':'Foo Bar Status Update'}) if hasattr(auth_system_module, 'send_message'): - self.assertNotEquals(u.update_status_template, None) + self.assertNotEqual(u.update_status_template, None) else: - self.assertEquals(u.update_status_template, None) + self.assertEqual(u.update_status_template, None) def test_eligibility(self): """ @@ -76,22 +75,18 @@ class UserModelTests(unittest.TestCase): FIXME: also test constraints on eligibility """ - for auth_system, auth_system_module in AUTH_SYSTEMS.iteritems(): + for auth_system, auth_system_module in AUTH_SYSTEMS.items(): u = models.User.update_or_create(user_type = auth_system, user_id = 'foobar_status_update', info={'name':'Foo Bar Status Update'}) self.assertTrue(u.is_eligible_for({'auth_system': auth_system})) def test_eq(self): - for auth_system, auth_system_module in AUTH_SYSTEMS.iteritems(): + for auth_system, auth_system_module in AUTH_SYSTEMS.items(): u = models.User.update_or_create(user_type = auth_system, user_id = 'foobar_eq', info={'name':'Foo Bar Status Update'}) u2 = models.User.update_or_create(user_type = auth_system, user_id = 'foobar_eq', info={'name':'Foo Bar Status Update'}) - self.assertEquals(u, u2) - + self.assertEqual(u, u2) -import views -import auth_systems.password as password_views -from django.urls import reverse # FIXME: login CSRF should make these tests more complicated # and should be tested for @@ -125,6 +120,6 @@ class UserBlackboxTests(TestCase): """using the test email backend""" self.test_user.send_message("testing subject", "testing body") - self.assertEquals(len(mail.outbox), 1) - self.assertEquals(mail.outbox[0].subject, "testing subject") - self.assertEquals(mail.outbox[0].to[0], "\"Foobar User\" <foobar-test@adida.net>") + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].subject, "testing subject") + self.assertEqual(mail.outbox[0].to[0], "\"Foobar User\" <foobar-test@adida.net>") diff --git a/helios_auth/urls.py b/helios_auth/urls.py index 5244e10b8a1e2f07f8ab712c3b8f3997e58bedfa..43f552dd808135eadfe1faa041cde7e95686596e 100644 --- a/helios_auth/urls.py +++ b/helios_auth/urls.py @@ -7,9 +7,8 @@ Ben Adida (ben@adida.net) from django.conf.urls import url -import url_names -import views from settings import AUTH_ENABLED_AUTH_SYSTEMS +from . import views, url_names urlpatterns = [ # basic static stuff @@ -24,10 +23,10 @@ urlpatterns = [ # password auth if 'password' in AUTH_ENABLED_AUTH_SYSTEMS: - from auth_systems.password import urlpatterns as password_patterns + from .auth_systems.password import urlpatterns as password_patterns urlpatterns.extend(password_patterns) # twitter if 'twitter' in AUTH_ENABLED_AUTH_SYSTEMS: - from auth_systems.twitter import urlpatterns as twitter_patterns + from .auth_systems.twitter import urlpatterns as twitter_patterns urlpatterns.extend(twitter_patterns) diff --git a/helios_auth/utils.py b/helios_auth/utils.py index f57dedf6e961e8598963049dca55f6cdaf211f00..dc723adce77a3f08c19e1a0438a8ae20f611e882 100644 --- a/helios_auth/utils.py +++ b/helios_auth/utils.py @@ -7,23 +7,30 @@ Some basic utils import json + ## JSON 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) - -def JSONtoDict(json): - x=json.loads(json) - return x - -def JSONFiletoDict(filename): - f = open(filename, 'r') - content = f.read() - f.close() - return JSONtoDict(content) - + return json.dumps(d, sort_keys=True) + + +def from_json(value): + if value == "" or value is None: + return None + if isinstance(value, str): + try: + return json.loads(value) + except Exception as e: + # import ast + # try: + # parsed_value = ast.literal_eval(parsed_value) + # except Exception as e1: + raise Exception("value is not JSON parseable, that's bad news") from e + return value + + +def JSONFiletoDict(filename): + with open(filename, 'r') as f: + content = f.read() + return from_json(content) diff --git a/helios_auth/view_utils.py b/helios_auth/view_utils.py index 6699ab28ab5030b4b8703ea4868f5ef1edd22a14..48b90c7806167ee9031a3c5e55a82f03adf9af37 100644 --- a/helios_auth/view_utils.py +++ b/helios_auth/view_utils.py @@ -52,7 +52,3 @@ def render_template_raw(request, template_name, values=None): vars_with_user = prepare_vars(request, values) return t.render(context=vars_with_user, request=request) - - -def render_json(json_txt): - return HttpResponse(json_txt) diff --git a/helios_auth/views.py b/helios_auth/views.py index f246dafd582ef696d6ba2351fad93d507f7924eb..73f23050ba2f9767a94da17a2e5b180578c4c6be 100644 --- a/helios_auth/views.py +++ b/helios_auth/views.py @@ -5,19 +5,19 @@ Ben Adida 2009-07-05 """ -import urllib -from django.urls import reverse +from urllib.parse import urlencode + from django.http import HttpResponseRedirect, HttpResponse +from django.urls import reverse -import helios_auth import settings -from auth_systems import AUTH_SYSTEMS -from auth_systems import password +from helios_auth import DEFAULT_AUTH_SYSTEM, ENABLED_AUTH_SYSTEMS from helios_auth.security import get_user from helios_auth.url_names import AUTH_INDEX, AUTH_START, AUTH_AFTER, AUTH_WHY, AUTH_AFTER_INTERVENTION -from models import User -from security import FIELDS_TO_SAVE -from view_utils import render_template, render_template_raw +from .auth_systems import AUTH_SYSTEMS, password +from .models import User +from .security import FIELDS_TO_SAVE +from .view_utils import render_template, render_template_raw def index(request): @@ -28,21 +28,21 @@ def index(request): user = get_user(request) # single auth system? - if len(helios_auth.ENABLED_AUTH_SYSTEMS) == 1 and not user: - return HttpResponseRedirect(reverse(AUTH_START, args=[helios_auth.ENABLED_AUTH_SYSTEMS[0]])+ '?return_url=' + request.GET.get('return_url', '')) + if len(ENABLED_AUTH_SYSTEMS) == 1 and not user: + return HttpResponseRedirect(reverse(AUTH_START, args=[ENABLED_AUTH_SYSTEMS[0]])+ '?return_url=' + request.GET.get('return_url', '')) - #if helios_auth.DEFAULT_AUTH_SYSTEM and not user: - # return HttpResponseRedirect(reverse(start, args=[helios_auth.DEFAULT_AUTH_SYSTEM])+ '?return_url=' + request.GET.get('return_url', '')) + #if DEFAULT_AUTH_SYSTEM and not user: + # return HttpResponseRedirect(reverse(start, args=[DEFAULT_AUTH_SYSTEM])+ '?return_url=' + request.GET.get('return_url', '')) default_auth_system_obj = None - if helios_auth.DEFAULT_AUTH_SYSTEM: - default_auth_system_obj = AUTH_SYSTEMS[helios_auth.DEFAULT_AUTH_SYSTEM] + if DEFAULT_AUTH_SYSTEM: + default_auth_system_obj = AUTH_SYSTEMS[DEFAULT_AUTH_SYSTEM] #form = password.LoginForm() return render_template(request, 'index', {'return_url' : request.GET.get('return_url', '/'), - 'enabled_auth_systems' : helios_auth.ENABLED_AUTH_SYSTEMS, - 'default_auth_system': helios_auth.DEFAULT_AUTH_SYSTEM, + 'enabled_auth_systems' : ENABLED_AUTH_SYSTEMS, + 'default_auth_system': DEFAULT_AUTH_SYSTEM, 'default_auth_system_obj': default_auth_system_obj}) def login_box_raw(request, return_url='/', auth_systems = None): @@ -50,20 +50,20 @@ def login_box_raw(request, return_url='/', auth_systems = None): a chunk of HTML that shows the various login options """ default_auth_system_obj = None - if helios_auth.DEFAULT_AUTH_SYSTEM: - default_auth_system_obj = AUTH_SYSTEMS[helios_auth.DEFAULT_AUTH_SYSTEM] + if DEFAULT_AUTH_SYSTEM: + default_auth_system_obj = AUTH_SYSTEMS[DEFAULT_AUTH_SYSTEM] # make sure that auth_systems includes only available and enabled auth systems if auth_systems is not None: - enabled_auth_systems = set(auth_systems).intersection(set(helios_auth.ENABLED_AUTH_SYSTEMS)).intersection(set(AUTH_SYSTEMS.keys())) + enabled_auth_systems = set(auth_systems).intersection(set(ENABLED_AUTH_SYSTEMS)).intersection(set(AUTH_SYSTEMS.keys())) else: - enabled_auth_systems = set(helios_auth.ENABLED_AUTH_SYSTEMS).intersection(set(AUTH_SYSTEMS.keys())) + enabled_auth_systems = set(ENABLED_AUTH_SYSTEMS).intersection(set(AUTH_SYSTEMS.keys())) form = password.LoginForm() return render_template_raw(request, 'login_box', { 'enabled_auth_systems': enabled_auth_systems, 'return_url': return_url, - 'default_auth_system': helios_auth.DEFAULT_AUTH_SYSTEM, 'default_auth_system_obj': default_auth_system_obj, + 'default_auth_system': DEFAULT_AUTH_SYSTEM, 'default_auth_system_obj': default_auth_system_obj, 'form' : form}) def do_local_logout(request): @@ -74,7 +74,7 @@ def do_local_logout(request): user = None - if request.session.has_key('user'): + if 'user' in request.session: user = request.session['user'] # 2010-08-14 be much more aggressive here @@ -151,7 +151,7 @@ def _do_auth(request): return HttpResponse("an error occurred trying to contact " + system_name +", try again later") def start(request, system_name): - if not (system_name in helios_auth.ENABLED_AUTH_SYSTEMS): + if not (system_name in ENABLED_AUTH_SYSTEMS): return HttpResponseRedirect(reverse(AUTH_INDEX)) # why is this here? Let's try without it @@ -173,7 +173,7 @@ def perms_why(request): def after(request): # which auth system were we using? - if not request.session.has_key('auth_system_name'): + if 'auth_system_name' not in request.session: do_local_logout(request) return HttpResponseRedirect("/") @@ -188,7 +188,7 @@ def after(request): request.session['user'] = user else: - return HttpResponseRedirect("%s?%s" % (reverse(AUTH_WHY), urllib.urlencode({'system_name' : request.session['auth_system_name']}))) + return HttpResponseRedirect("%s?%s" % (reverse(AUTH_WHY), urlencode({'system_name' : request.session['auth_system_name']}))) # does the auth system want to present an additional view? # this is, for example, to prompt the user to follow @heliosvoting @@ -203,7 +203,7 @@ def after(request): def after_intervention(request): return_url = "/" - if request.session.has_key('auth_return_url'): + if 'auth_return_url' in request.session: return_url = request.session['auth_return_url'] del request.session['auth_return_url'] return HttpResponseRedirect(settings.URL_HOST + return_url) diff --git a/requirements.txt b/requirements.txt index d779153ddf26e55b5d21249a094de8481a5063f0..414d5f26982d0769a3988be987cb698c15412f43 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,21 +4,20 @@ celery==4.2.1 django-picklefield==0.3.0 kombu==4.2.0 html5lib==0.999 -psycopg2==2.7.3.2 -pyparsing==1.5.7 +psycopg2==2.8.3 +pyparsing==2.0.0 python-dateutil>=1.5 -python-openid==2.2.5 -wsgiref==0.1.2 +python3-openid==3.0.10 gunicorn==19.9 requests==2.21.0 -unicodecsv==0.9.0 +unicodecsv==0.14.1 dj_database_url==0.3.0 django_webtest>=1.9 webtest==2.0.18 bleach==1.4.1 -boto==2.27.0 -django-ses==0.6.0 -validate_email==1.2 -oauth2client==1.2 +boto==2.49.0 +django-ses==0.8.10 +py3-validate-email==0.1.11 +oauth2client==4.1.3 rollbar==0.12.1 pycryptodome==3.8.2 diff --git a/server_ui/urls.py b/server_ui/urls.py index e691c07afe7b29c0b347e5bde5ea75e9addd65c7..aad77654242dcfa268cc23b7103de800b68b9dfa 100644 --- a/server_ui/urls.py +++ b/server_ui/urls.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from django.conf.urls import url -from views import home, about, docs, faq, privacy +from .views import home, about, docs, faq, privacy urlpatterns = [ url(r'^$', home), diff --git a/server_ui/view_utils.py b/server_ui/view_utils.py index df947214620fc0169b9569767a84e633cd0e7615..f499603e108fdbb3db9a352df2e0fb6efb4ab2e9 100644 --- a/server_ui/view_utils.py +++ b/server_ui/view_utils.py @@ -20,7 +20,7 @@ def render_template(request, template_name, values = None): vars_with_user['CURRENT_URL'] = request.path # csrf protection - if request.session.has_key('csrf_token'): + if 'csrf_token' in request.session: vars_with_user['csrf_token'] = request.session['csrf_token'] return render_to_response('server_ui/templates/%s.html' % template_name, vars_with_user) diff --git a/server_ui/views.py b/server_ui/views.py index 190d902aa71f4c0722ed68a787055a4f978ba7c8..2dbaa1ad2b01c2e72dbb4976cb83853a5cb7d554 100644 --- a/server_ui/views.py +++ b/server_ui/views.py @@ -3,15 +3,15 @@ server_ui specific views """ import copy + from django.conf import settings import helios_auth.views as auth_views from helios.models import Election from helios.security import can_create_election from helios_auth.security import get_user -from view_utils import render_template -import glue - +from . import glue +from .view_utils import render_template glue.glue() # actually apply glue helios.view <-> helios.signals diff --git a/settings.py b/settings.py index 32769792e235207c8e79a46942056972dcacf343..13b9069f694427b77490bda31e6d1b4e820f596c 100644 --- a/settings.py +++ b/settings.py @@ -9,7 +9,7 @@ TESTING = 'test' in sys.argv # go through environment variables and override them def get_from_env(var, default): - if not TESTING and os.environ.has_key(var): + if not TESTING and var in os.environ: return os.environ[var] else: return default @@ -39,7 +39,7 @@ DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'helios' - } + }, } # override if we have an env variable @@ -277,7 +277,7 @@ if TESTING: # Rollbar Error Logging ROLLBAR_ACCESS_TOKEN = get_from_env('ROLLBAR_ACCESS_TOKEN', None) if ROLLBAR_ACCESS_TOKEN: - print "setting up rollbar" + print("setting up rollbar") MIDDLEWARE += ['rollbar.contrib.django.middleware.RollbarNotifierMiddleware',] ROLLBAR = { 'access_token': ROLLBAR_ACCESS_TOKEN,