diff --git a/config.py b/config.py index ca0b3cd..353a70d 100644 --- a/config.py +++ b/config.py @@ -6,7 +6,7 @@ Configuration overrides for settings.py import os import sys from django.urls import reverse_lazy -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.contrib.messages import constants as message_constants from ivatar.settings import BASE_DIR diff --git a/import_libravatar.py b/import_libravatar.py index c000633..424850b 100644 --- a/import_libravatar.py +++ b/import_libravatar.py @@ -1,7 +1,8 @@ #!/usr/bin/env python -''' +# -*- coding: utf-8 -*- +""" Import the whole libravatar export -''' +""" import os from os.path import isfile, isdir, join @@ -9,13 +10,18 @@ import sys import base64 from io import BytesIO import django -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ivatar.settings") # pylint: disable=wrong-import-position + +os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", "ivatar.settings" +) # pylint: disable=wrong-import-position django.setup() # pylint: disable=wrong-import-position from django.contrib.auth.models import User from PIL import Image from django_openid_auth.models import UserOpenID from ivatar.settings import JPEG_QUALITY -from ivatar.ivataraccount.read_libravatar_export import read_gzdata as libravatar_read_gzdata +from ivatar.ivataraccount.read_libravatar_export import ( + read_gzdata as libravatar_read_gzdata, +) from ivatar.ivataraccount.models import ConfirmedEmail from ivatar.ivataraccount.models import ConfirmedOpenId from ivatar.ivataraccount.models import Photo @@ -26,54 +32,63 @@ if len(sys.argv) < 2: exit(-255) if not isdir(sys.argv[1]): - print("First argument to '%s' must be a directory containing the exports" % sys.argv[0]) + print( + "First argument to '%s' must be a directory containing the exports" + % sys.argv[0] + ) exit(-255) PATH = sys.argv[1] for file in os.listdir(PATH): - if not file.endswith('.xml.gz'): + if not file.endswith(".xml.gz"): continue if isfile(join(PATH, file)): - fh = open(join(PATH, file), 'rb') + fh = open(join(PATH, file), "rb") items = libravatar_read_gzdata(fh.read()) - print('Adding user "%s"' % items['username']) - (user, created) = User.objects.get_or_create(username=items['username']) - user.password = items['password'] + print('Adding user "%s"' % items["username"]) + (user, created) = User.objects.get_or_create(username=items["username"]) + user.password = items["password"] user.save() saved_photos = {} - for photo in items['photos']: - photo_id = photo['id'] - data = base64.decodebytes(bytes(photo['data'], 'utf-8')) + for photo in items["photos"]: + photo_id = photo["id"] + data = base64.decodebytes(bytes(photo["data"], "utf-8")) pilobj = Image.open(BytesIO(data)) out = BytesIO() pilobj.save(out, pilobj.format, quality=JPEG_QUALITY) out.seek(0) photo = Photo() photo.user = user - photo.ip_address = '0.0.0.0' + photo.ip_address = "0.0.0.0" photo.format = file_format(pilobj.format) photo.data = out.read() photo.save() saved_photos[photo_id] = photo - for email in items['emails']: + for email in items["emails"]: try: - ConfirmedEmail.objects.get_or_create(email=email['email'], user=user, - photo=saved_photos.get(email['photo_id'])) - except django.db.utils.IntegrityError: - print('%s not unique?' % email['email']) - - for openid in items['openids']: - try: - ConfirmedOpenId.objects.get_or_create(openid=openid['openid'], user=user, - photo=saved_photos.get(openid['photo_id'])) # pylint: disable=no-member - UserOpenID.objects.get_or_create( - user_id=user.id, - claimed_id=openid['openid'], - display_id=openid['openid'], + ConfirmedEmail.objects.get_or_create( + email=email["email"], + user=user, + photo=saved_photos.get(email["photo_id"]), ) except django.db.utils.IntegrityError: - print('%s not unique?' % openid['openid']) + print("%s not unique?" % email["email"]) + + for openid in items["openids"]: + try: + ConfirmedOpenId.objects.get_or_create( + openid=openid["openid"], + user=user, + photo=saved_photos.get(openid["photo_id"]), + ) # pylint: disable=no-member + UserOpenID.objects.get_or_create( + user_id=user.id, + claimed_id=openid["openid"], + display_id=openid["openid"], + ) + except django.db.utils.IntegrityError: + print("%s not unique?" % openid["openid"]) fh.close() diff --git a/ivatar/ivataraccount/__init__.py b/ivatar/ivataraccount/__init__.py index aa92007..a8a5815 100644 --- a/ivatar/ivataraccount/__init__.py +++ b/ivatar/ivataraccount/__init__.py @@ -1,4 +1,5 @@ -''' +# -*- coding: utf-8 -*- +""" Module init -''' +""" app_label = __name__ # pylint: disable=invalid-name diff --git a/ivatar/ivataraccount/admin.py b/ivatar/ivataraccount/admin.py index 2fda579..adc28cd 100644 --- a/ivatar/ivataraccount/admin.py +++ b/ivatar/ivataraccount/admin.py @@ -1,12 +1,13 @@ -''' +# -*- coding: utf-8 -*- +""" Register models in admin -''' +""" from django.contrib import admin -from . models import Photo, ConfirmedEmail, UnconfirmedEmail -from . models import ConfirmedOpenId, UnconfirmedOpenId -from . models import OpenIDNonce, OpenIDAssociation -from . models import UserPreference +from .models import Photo, ConfirmedEmail, UnconfirmedEmail +from .models import ConfirmedOpenId, UnconfirmedOpenId +from .models import OpenIDNonce, OpenIDAssociation +from .models import UserPreference # Register models in admin admin.site.register(Photo) diff --git a/ivatar/ivataraccount/forms.py b/ivatar/ivataraccount/forms.py index 2f98dfe..36b302e 100644 --- a/ivatar/ivataraccount/forms.py +++ b/ivatar/ivataraccount/forms.py @@ -1,112 +1,117 @@ -''' +# -*- coding: utf-8 -*- +""" Classes for our ivatar.ivataraccount.forms -''' +""" from urllib.parse import urlsplit, urlunsplit from django import forms -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from ipware import get_client_ip from ivatar import settings from ivatar.settings import MIN_LENGTH_EMAIL, MAX_LENGTH_EMAIL from ivatar.settings import MIN_LENGTH_URL, MAX_LENGTH_URL -from . models import UnconfirmedEmail, ConfirmedEmail, Photo -from . models import UnconfirmedOpenId, ConfirmedOpenId -from . models import UserPreference +from .models import UnconfirmedEmail, ConfirmedEmail, Photo +from .models import UnconfirmedOpenId, ConfirmedOpenId +from .models import UserPreference MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT = 5 class AddEmailForm(forms.Form): - ''' + """ Form to handle adding email addresses - ''' + """ + email = forms.EmailField( - label=_('Email'), + label=_("Email"), min_length=MIN_LENGTH_EMAIL, max_length=MAX_LENGTH_EMAIL, ) def clean_email(self): - ''' + """ Enforce lowercase email - ''' + """ # TODO: Domain restriction as in libravatar? - return self.cleaned_data['email'].lower() + return self.cleaned_data["email"].lower() def save(self, request): - ''' + """ Save the model, ensuring some safety - ''' + """ user = request.user # Enforce the maximum number of unconfirmed emails a user can have num_unconfirmed = user.unconfirmedemail_set.count() max_num_unconfirmed_emails = getattr( - settings, - 'MAX_NUM_UNCONFIRMED_EMAILS', - MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT) + settings, "MAX_NUM_UNCONFIRMED_EMAILS", MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT + ) if num_unconfirmed >= max_num_unconfirmed_emails: - self.add_error(None, _('Too many unconfirmed mail addresses!')) + self.add_error(None, _("Too many unconfirmed mail addresses!")) return False # Check whether or not a confirmation email has been # sent by this user already if UnconfirmedEmail.objects.filter( # pylint: disable=no-member - user=user, email=self.cleaned_data['email']).exists(): - self.add_error( - 'email', - _('Address already added, currently unconfirmed')) + user=user, email=self.cleaned_data["email"] + ).exists(): + self.add_error("email", _("Address already added, currently unconfirmed")) return False # Check whether or not the email is already confirmed (by someone) - check_mail = ConfirmedEmail.objects.filter( - email=self.cleaned_data['email']) + check_mail = ConfirmedEmail.objects.filter(email=self.cleaned_data["email"]) if check_mail.exists(): - msg = _('Address already confirmed (by someone else)') + msg = _("Address already confirmed (by someone else)") if check_mail.first().user == request.user: - msg = _('Address already confirmed (by you)') - self.add_error('email', msg) + msg = _("Address already confirmed (by you)") + self.add_error("email", msg) return False unconfirmed = UnconfirmedEmail() - unconfirmed.email = self.cleaned_data['email'] + unconfirmed.email = self.cleaned_data["email"] unconfirmed.user = user unconfirmed.save() - unconfirmed.send_confirmation_mail(url=request.build_absolute_uri('/')[:-1]) + unconfirmed.send_confirmation_mail(url=request.build_absolute_uri("/")[:-1]) return True class UploadPhotoForm(forms.Form): - ''' + """ Form handling photo upload - ''' + """ + photo = forms.FileField( - label=_('Photo'), - error_messages={'required': _('You must choose an image to upload.')}) + label=_("Photo"), + error_messages={"required": _("You must choose an image to upload.")}, + ) not_porn = forms.BooleanField( - label=_('suitable for all ages (i.e. no offensive content)'), + label=_("suitable for all ages (i.e. no offensive content)"), required=True, error_messages={ - 'required': - _('We only host "G-rated" images and so this field must be checked.') - }) + "required": _( + 'We only host "G-rated" images and so this field must be checked.' + ) + }, + ) can_distribute = forms.BooleanField( - label=_('can be freely copied'), + label=_("can be freely copied"), required=True, error_messages={ - 'required': - _('This field must be checked since we need to be able to distribute photos to third parties.') - }) + "required": _( + "This field must be checked since we need to be able to distribute photos to third parties." + ) + }, + ) @staticmethod def save(request, data): - ''' + """ Save the model and assign it to the current user - ''' + """ # Link this file to the user's profile photo = Photo() photo.user = request.user @@ -119,47 +124,48 @@ class UploadPhotoForm(forms.Form): class AddOpenIDForm(forms.Form): - ''' + """ Form to handle adding OpenID - ''' + """ + openid = forms.URLField( - label=_('OpenID'), + label=_("OpenID"), min_length=MIN_LENGTH_URL, max_length=MAX_LENGTH_URL, - initial='http://' + initial="http://", ) def clean_openid(self): - ''' + """ Enforce restrictions - ''' + """ # Lowercase hostname port of the URL - url = urlsplit(self.cleaned_data['openid']) + url = urlsplit(self.cleaned_data["openid"]) data = urlunsplit( - (url.scheme.lower(), url.netloc.lower(), url.path, - url.query, url.fragment)) + (url.scheme.lower(), url.netloc.lower(), url.path, url.query, url.fragment) + ) # TODO: Domain restriction as in libravatar? return data def save(self, user): - ''' + """ Save the model, ensuring some safety - ''' + """ if ConfirmedOpenId.objects.filter( # pylint: disable=no-member - openid=self.cleaned_data['openid']).exists(): - self.add_error('openid', _('OpenID already added and confirmed!')) + openid=self.cleaned_data["openid"] + ).exists(): + self.add_error("openid", _("OpenID already added and confirmed!")) return False if UnconfirmedOpenId.objects.filter( # pylint: disable=no-member - openid=self.cleaned_data['openid']).exists(): - self.add_error( - 'openid', - _('OpenID already added, but not confirmed yet!')) + openid=self.cleaned_data["openid"] + ).exists(): + self.add_error("openid", _("OpenID already added, but not confirmed yet!")) return False unconfirmed = UnconfirmedOpenId() - unconfirmed.openid = self.cleaned_data['openid'] + unconfirmed.openid = self.cleaned_data["openid"] unconfirmed.user = user unconfirmed.save() @@ -167,40 +173,50 @@ class AddOpenIDForm(forms.Form): class UpdatePreferenceForm(forms.ModelForm): - ''' + """ Form for updating user preferences - ''' + """ class Meta: # pylint: disable=too-few-public-methods - ''' + """ Meta class for UpdatePreferenceForm - ''' + """ + model = UserPreference - fields = ['theme'] + fields = ["theme"] class UploadLibravatarExportForm(forms.Form): - ''' + """ Form handling libravatar user export upload - ''' + """ + export_file = forms.FileField( - label=_('Export file'), - error_messages={'required': _('You must choose an export file to upload.')}) + label=_("Export file"), + error_messages={"required": _("You must choose an export file to upload.")}, + ) not_porn = forms.BooleanField( - label=_('suitable for all ages (i.e. no offensive content)'), + label=_("suitable for all ages (i.e. no offensive content)"), required=True, error_messages={ - 'required': - _('We only host "G-rated" images and so this field must be checked.') - }) + "required": _( + 'We only host "G-rated" images and so this field must be checked.' + ) + }, + ) can_distribute = forms.BooleanField( - label=_('can be freely copied'), + label=_("can be freely copied"), required=True, error_messages={ - 'required': - _('This field must be checked since we need to be able to\ - distribute photos to third parties.') - }) + "required": _( + "This field must be checked since we need to be able to\ + distribute photos to third parties." + ) + }, + ) + class DeleteAccountForm(forms.Form): - password = forms.CharField(label=_('Password'), required=False, widget=forms.PasswordInput()) + password = forms.CharField( + label=_("Password"), required=False, widget=forms.PasswordInput() + ) diff --git a/ivatar/ivataraccount/gravatar.py b/ivatar/ivataraccount/gravatar.py index df1cfe6..18ca9a7 100644 --- a/ivatar/ivataraccount/gravatar.py +++ b/ivatar/ivataraccount/gravatar.py @@ -1,53 +1,58 @@ -''' +# -*- coding: utf-8 -*- +""" Helper method to fetch Gravatar image -''' +""" from ssl import SSLError from urllib.request import urlopen, HTTPError, URLError import hashlib -from .. settings import AVATAR_MAX_SIZE +from ..settings import AVATAR_MAX_SIZE URL_TIMEOUT = 5 # in seconds def get_photo(email): - ''' + """ Fetch photo from Gravatar, given an email address - ''' - hash_object = hashlib.new('md5') - hash_object.update(email.lower().encode('utf-8')) - thumbnail_url = 'https://secure.gravatar.com/avatar/' + \ - hash_object.hexdigest() + '?s=%i&d=404' % AVATAR_MAX_SIZE - image_url = 'https://secure.gravatar.com/avatar/' + hash_object.hexdigest( - ) + '?s=512&d=404' + """ + hash_object = hashlib.new("md5") + hash_object.update(email.lower().encode("utf-8")) + thumbnail_url = ( + "https://secure.gravatar.com/avatar/" + + hash_object.hexdigest() + + "?s=%i&d=404" % AVATAR_MAX_SIZE + ) + image_url = ( + "https://secure.gravatar.com/avatar/" + hash_object.hexdigest() + "?s=512&d=404" + ) # Will redirect to the public profile URL if it exists - service_url = 'http://www.gravatar.com/' + hash_object.hexdigest() + service_url = "http://www.gravatar.com/" + hash_object.hexdigest() try: urlopen(image_url, timeout=URL_TIMEOUT) except HTTPError as exc: if exc.code != 404 and exc.code != 503: print( # pragma: no cover - 'Gravatar fetch failed with an unexpected %s HTTP error' % - exc.code) + "Gravatar fetch failed with an unexpected %s HTTP error" % exc.code + ) return False except URLError as exc: # pragma: no cover print( - 'Gravatar fetch failed with URL error: %s' % - exc.reason) # pragma: no cover + "Gravatar fetch failed with URL error: %s" % exc.reason + ) # pragma: no cover return False # pragma: no cover - except SSLError as exc: # pragma: no cover + except SSLError as exc: # pragma: no cover print( - 'Gravatar fetch failed with SSL error: %s' % - exc.reason) # pragma: no cover + "Gravatar fetch failed with SSL error: %s" % exc.reason + ) # pragma: no cover return False # pragma: no cover return { - 'thumbnail_url': thumbnail_url, - 'image_url': image_url, - 'width': AVATAR_MAX_SIZE, - 'height': AVATAR_MAX_SIZE, - 'service_url': service_url, - 'service_name': 'Gravatar' + "thumbnail_url": thumbnail_url, + "image_url": image_url, + "width": AVATAR_MAX_SIZE, + "height": AVATAR_MAX_SIZE, + "service_url": service_url, + "service_name": "Gravatar", } diff --git a/ivatar/ivataraccount/models.py b/ivatar/ivataraccount/models.py index 478619f..689fd8a 100644 --- a/ivatar/ivataraccount/models.py +++ b/ivatar/ivataraccount/models.py @@ -19,7 +19,7 @@ from django.db import models from django.utils import timezone from django.http import HttpResponseRedirect from django.urls import reverse_lazy, reverse -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ObjectDoesNotExist from django.core.mail import send_mail from django.template.loader import render_to_string diff --git a/ivatar/ivataraccount/views.py b/ivatar/ivataraccount/views.py index 1758730..2bb7711 100644 --- a/ivatar/ivataraccount/views.py +++ b/ivatar/ivataraccount/views.py @@ -27,7 +27,7 @@ from django.contrib.auth.views import LoginView from django.contrib.auth.views import ( PasswordResetView as PasswordResetViewOriginal, ) -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.http import HttpResponseRedirect, HttpResponse from django.urls import reverse_lazy, reverse from django.shortcuts import render diff --git a/ivatar/settings.py b/ivatar/settings.py index 82cf3f0..a391353 100644 --- a/ivatar/settings.py +++ b/ivatar/settings.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Django settings for ivatar project. """ @@ -6,7 +7,7 @@ import os import logging log_level = logging.DEBUG # pylint: disable=invalid-name -logger = logging.getLogger('ivatar') # pylint: disable=invalid-name +logger = logging.getLogger("ivatar") # pylint: disable=invalid-name logger.setLevel(log_level) PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__)) @@ -14,7 +15,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '=v(+-^t#ahv^a&&e)uf36g8algj$d1@6ou^w(r0@%)#8mlc*zk' +SECRET_KEY = "=v(+-^t#ahv^a&&e)uf36g8algj$d1@6ou^w(r0@%)#8mlc*zk" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -25,52 +26,52 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -ROOT_URLCONF = 'ivatar.urls' +ROOT_URLCONF = "ivatar.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] -WSGI_APPLICATION = 'ivatar.wsgi.application' +WSGI_APPLICATION = "ivatar.wsgi.application" # Database # https://docs.djangoproject.com/en/2.0/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } } @@ -80,16 +81,16 @@ DATABASES = { AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', # noqa + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", # noqa }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', # noqa + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", # noqa }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', # noqa + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", # noqa }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', # noqa + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", # noqa }, ] @@ -97,9 +98,9 @@ AUTH_PASSWORD_VALIDATORS = [ # Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -109,15 +110,10 @@ USE_TZ = True # Static files configuration (esp. req. during dev.) -PROJECT_ROOT = os.path.abspath( - os.path.join( - os.path.dirname(__file__), - os.pardir - ) -) -STATIC_URL = '/static/' -STATIC_ROOT = os.path.join(BASE_DIR, 'static') +PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) +STATIC_URL = "/static/" +STATIC_ROOT = os.path.join(BASE_DIR, "static") -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" from config import * # pylint: disable=wildcard-import,wrong-import-position,unused-wildcard-import diff --git a/ivatar/test_auxiliary.py b/ivatar/test_auxiliary.py index ddf8b2d..50e9811 100644 --- a/ivatar/test_auxiliary.py +++ b/ivatar/test_auxiliary.py @@ -1,8 +1,9 @@ -''' +# -*- coding: utf-8 -*- +""" Test various other parts of ivatar/libravatar in order to increase the overall test coverage. Test in here, didn't fit anywhere else. -''' +""" from django.test import TestCase from django.contrib.auth.models import User @@ -12,35 +13,35 @@ from ivatar.ivataraccount.models import pil_format, UserPreference class Tester(TestCase): - ''' + """ Main test class - ''' + """ + user = None username = random_string() def setUp(self): - ''' + """ Prepare tests. - Create user - ''' + """ self.user = User.objects.create_user( username=self.username, ) def test_pil_format(self): - ''' + """ Test pil format function - ''' - self.assertEqual(pil_format('jpg'), 'JPEG') - self.assertEqual(pil_format('jpeg'), 'JPEG') - self.assertEqual(pil_format('png'), 'PNG') - self.assertEqual(pil_format('gif'), 'GIF') - self.assertEqual(pil_format('abc'), None) + """ + self.assertEqual(pil_format("jpg"), "JPEG") + self.assertEqual(pil_format("jpeg"), "JPEG") + self.assertEqual(pil_format("png"), "PNG") + self.assertEqual(pil_format("gif"), "GIF") + self.assertEqual(pil_format("abc"), None) def test_userprefs_str(self): - ''' + """ Test if str representation of UserPreferences is as expected - ''' - up = UserPreference(theme='default', user=self.user) + """ + up = UserPreference(theme="default", user=self.user) print(up) - diff --git a/ivatar/test_static_pages.py b/ivatar/test_static_pages.py index 89596c0..280679d 100644 --- a/ivatar/test_static_pages.py +++ b/ivatar/test_static_pages.py @@ -1,6 +1,7 @@ -''' +# -*- coding: utf-8 -*- +""" Test our views in ivatar.ivataraccount.views and ivatar.views -''' +""" # pylint: disable=too-many-lines import os import django @@ -11,33 +12,34 @@ from django.contrib.auth.models import User from ivatar.utils import random_string -os.environ['DJANGO_SETTINGS_MODULE'] = 'ivatar.settings' +os.environ["DJANGO_SETTINGS_MODULE"] = "ivatar.settings" django.setup() class Tester(TestCase): # pylint: disable=too-many-public-methods - ''' + """ Main test class - ''' + """ + client = Client() user = None username = random_string() password = random_string() - email = '%s@%s.%s' % (username, random_string(), random_string(2)) + email = "%s@%s.%s" % (username, random_string(), random_string(2)) # Dunno why random tld doesn't work, but I'm too lazy now to investigate - openid = 'http://%s.%s.%s/' % (username, random_string(), 'org') + openid = "http://%s.%s.%s/" % (username, random_string(), "org") def login(self): - ''' + """ Login as user - ''' + """ self.client.login(username=self.username, password=self.password) def setUp(self): - ''' + """ Prepare for tests. - Create user - ''' + """ self.user = User.objects.create_user( username=self.username, password=self.password, @@ -47,19 +49,19 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods """ Test contact page """ - response = self.client.get(reverse('contact')) - self.assertEqual(response.status_code, 200, 'no 200 ok?') + response = self.client.get(reverse("contact")) + self.assertEqual(response.status_code, 200, "no 200 ok?") def test_description_page(self): """ Test description page """ - response = self.client.get(reverse('description')) - self.assertEqual(response.status_code, 200, 'no 200 ok?') + response = self.client.get(reverse("description")) + self.assertEqual(response.status_code, 200, "no 200 ok?") def test_security_page(self): """ Test security page """ - response = self.client.get(reverse('security')) - self.assertEqual(response.status_code, 200, 'no 200 ok?') + response = self.client.get(reverse("security")) + self.assertEqual(response.status_code, 200, "no 200 ok?") diff --git a/ivatar/test_utils.py b/ivatar/test_utils.py index 773a2fa..1436921 100644 --- a/ivatar/test_utils.py +++ b/ivatar/test_utils.py @@ -1,6 +1,7 @@ -''' +# -*- coding: utf-8 -*- +""" Test our utils from ivatar.utils -''' +""" from django.test import TestCase @@ -8,18 +9,18 @@ from ivatar.utils import openid_variations class Tester(TestCase): - ''' + """ Main test class - ''' + """ def test_openid_variations(self): - ''' + """ Test if the OpenID variation "generator" does the correct thing - ''' - openid0 = 'http://user.url/' - openid1 = 'http://user.url' - openid2 = 'https://user.url/' - openid3 = 'https://user.url' + """ + openid0 = "http://user.url/" + openid1 = "http://user.url" + openid2 = "https://user.url/" + openid3 = "https://user.url" # First variation self.assertEqual(openid_variations(openid0)[0], openid0) diff --git a/ivatar/test_views.py b/ivatar/test_views.py index 5631b8f..1f3ec49 100644 --- a/ivatar/test_views.py +++ b/ivatar/test_views.py @@ -1,6 +1,7 @@ -''' +# -*- coding: utf-8 -*- +""" Test our views in ivatar.ivataraccount.views and ivatar.views -''' +""" # pylint: disable=too-many-lines import os import django @@ -10,33 +11,34 @@ from django.contrib.auth.models import User from ivatar.utils import random_string -os.environ['DJANGO_SETTINGS_MODULE'] = 'ivatar.settings' +os.environ["DJANGO_SETTINGS_MODULE"] = "ivatar.settings" django.setup() class Tester(TestCase): # pylint: disable=too-many-public-methods - ''' + """ Main test class - ''' + """ + client = Client() user = None username = random_string() password = random_string() - email = '%s@%s.%s' % (username, random_string(), random_string(2)) + email = "%s@%s.%s" % (username, random_string(), random_string(2)) # Dunno why random tld doesn't work, but I'm too lazy now to investigate - openid = 'http://%s.%s.%s/' % (username, random_string(), 'org') + openid = "http://%s.%s.%s/" % (username, random_string(), "org") def login(self): - ''' + """ Login as user - ''' + """ self.client.login(username=self.username, password=self.password) def setUp(self): - ''' + """ Prepare for tests. - Create user - ''' + """ self.user = User.objects.create_user( username=self.username, password=self.password, @@ -46,8 +48,9 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods """ Test incorrect digest """ - response = self.client.get('/avatar/%s' % 'x'*65, follow=True) + response = self.client.get("/avatar/%s" % "x" * 65, follow=True) self.assertRedirects( response=response, - expected_url='/static/img/deadbeef.png', - msg_prefix='Why does an invalid hash not redirect to deadbeef?') + expected_url="/static/img/deadbeef.png", + msg_prefix="Why does an invalid hash not redirect to deadbeef?", + ) diff --git a/ivatar/test_wsgi.py b/ivatar/test_wsgi.py index e2da514..73a33e8 100644 --- a/ivatar/test_wsgi.py +++ b/ivatar/test_wsgi.py @@ -1,22 +1,27 @@ -''' +# -*- coding: utf-8 -*- +""" Unit tests for WSGI -''' +""" import unittest import os import django -os.environ['DJANGO_SETTINGS_MODULE'] = 'ivatar.settings' + +os.environ["DJANGO_SETTINGS_MODULE"] = "ivatar.settings" django.setup() class TestCase(unittest.TestCase): - ''' + """ Simple testcase to see if WSGI loads correctly - ''' + """ + def test_run_wsgi(self): - ''' + """ Run wsgi import - ''' + """ import ivatar.wsgi # pylint: disable=import-outside-toplevel - self.assertEqual(ivatar.wsgi.application.__class__, - django.core.handlers.wsgi.WSGIHandler) + + self.assertEqual( + ivatar.wsgi.application.__class__, django.core.handlers.wsgi.WSGIHandler + ) diff --git a/ivatar/tools/forms.py b/ivatar/tools/forms.py index 3e895a5..5d06894 100644 --- a/ivatar/tools/forms.py +++ b/ivatar/tools/forms.py @@ -1,8 +1,9 @@ -''' +# -*- coding: utf-8 -*- +""" Classes for our ivatar.tools.forms -''' +""" from django import forms -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError from django.forms.utils import ErrorList @@ -12,45 +13,40 @@ from ivatar.settings import MIN_LENGTH_EMAIL, MAX_LENGTH_EMAIL class CheckDomainForm(forms.Form): - ''' + """ Form handling domain check - ''' + """ + domain = forms.CharField( - label=_('Domain'), + label=_("Domain"), required=True, - error_messages={ - 'required': - _('Cannot check without a domain name.') - } + error_messages={"required": _("Cannot check without a domain name.")}, ) class CheckForm(forms.Form): - ''' + """ Form handling check - ''' + """ + mail = forms.EmailField( - label=_('E-Mail'), + label=_("E-Mail"), required=False, min_length=MIN_LENGTH_EMAIL, max_length=MAX_LENGTH_EMAIL, - error_messages={ - 'required': - _('Cannot check without a domain name.') - }) + error_messages={"required": _("Cannot check without a domain name.")}, + ) openid = forms.CharField( - label=_('OpenID'), + label=_("OpenID"), required=False, min_length=MIN_LENGTH_URL, max_length=MAX_LENGTH_URL, - error_messages={ - 'required': - _('Cannot check without an openid name.') - }) + error_messages={"required": _("Cannot check without an openid name.")}, + ) size = forms.IntegerField( - label=_('Size'), + label=_("Size"), initial=80, min_value=5, max_value=AVATAR_MAX_SIZE, @@ -58,24 +54,24 @@ class CheckForm(forms.Form): ) default_opt = forms.ChoiceField( - label=_('Default'), + label=_("Default"), required=False, widget=forms.RadioSelect, - choices = [ - ('retro', _('Retro style (similar to GitHub)')), - ('robohash', _('Roboter style')), - ('pagan', _('Retro adventure character')), - ('wavatar', _('Wavatar style')), - ('monsterid', _('Monster style')), - ('identicon', _('Identicon style')), - ('mm', _('Mystery man')), - ('mmng', _('Mystery man NextGen')), - ('none', _('None')), + choices=[ + ("retro", _("Retro style (similar to GitHub)")), + ("robohash", _("Roboter style")), + ("pagan", _("Retro adventure character")), + ("wavatar", _("Wavatar style")), + ("monsterid", _("Monster style")), + ("identicon", _("Identicon style")), + ("mm", _("Mystery man")), + ("mmng", _("Mystery man NextGen")), + ("none", _("None")), ], ) default_url = forms.URLField( - label=_('Default URL'), + label=_("Default URL"), min_length=1, max_length=MAX_LENGTH_URL, required=False, @@ -83,28 +79,27 @@ class CheckForm(forms.Form): def clean(self): self.cleaned_data = super().clean() - mail = self.cleaned_data.get('mail') - openid = self.cleaned_data.get('openid') - default_url = self.cleaned_data.get('default_url') - default_opt = self.cleaned_data.get('default_opt') - if default_url and default_opt and default_opt != 'none': - if not 'default_url' in self._errors: - self._errors['default_url'] = ErrorList() - if not 'default_opt' in self._errors: - self._errors['default_opt'] = ErrorList() + mail = self.cleaned_data.get("mail") + openid = self.cleaned_data.get("openid") + default_url = self.cleaned_data.get("default_url") + default_opt = self.cleaned_data.get("default_opt") + if default_url and default_opt and default_opt != "none": + if "default_url" not in self._errors: + self._errors["default_url"] = ErrorList() + if "default_opt" not in self._errors: + self._errors["default_opt"] = ErrorList() - errstring = _('Only default URL OR default keyword may be specified') - self._errors['default_url'].append(errstring) - self._errors['default_opt'].append(errstring) + errstring = _("Only default URL OR default keyword may be specified") + self._errors["default_url"].append(errstring) + self._errors["default_opt"].append(errstring) if not mail and not openid: - raise ValidationError(_('Either OpenID or mail must be specified')) + raise ValidationError(_("Either OpenID or mail must be specified")) return self.cleaned_data def clean_openid(self): - data = self.cleaned_data['openid'] + data = self.cleaned_data["openid"] return data.lower() def clean_mail(self): - data = self.cleaned_data['mail'] - print(data) + data = self.cleaned_data["mail"] return data.lower() diff --git a/ivatar/tools/test_views.py b/ivatar/tools/test_views.py index 4520efb..1c2647b 100644 --- a/ivatar/tools/test_views.py +++ b/ivatar/tools/test_views.py @@ -1,57 +1,48 @@ -''' +# -*- coding: utf-8 -*- +""" Test our views in ivatar.ivataraccount.views and ivatar.views -''' +""" # pylint: disable=too-many-lines -from urllib.parse import urlsplit -from io import BytesIO -import io import os import django from django.test import TestCase from django.test import Client from django.urls import reverse from django.contrib.auth.models import User -from django.contrib.auth import authenticate -import hashlib -from libravatar import libravatar_url - -from PIL import Image - -os.environ['DJANGO_SETTINGS_MODULE'] = 'ivatar.settings' +os.environ["DJANGO_SETTINGS_MODULE"] = "ivatar.settings" django.setup() # pylint: disable=wrong-import-position -from ivatar import settings -from ivatar.ivataraccount.forms import MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT -from ivatar.ivataraccount.models import Photo, ConfirmedOpenId from ivatar.utils import random_string + # pylint: enable=wrong-import-position class Tester(TestCase): # pylint: disable=too-many-public-methods - ''' + """ Main test class - ''' + """ + client = Client() user = None username = random_string() password = random_string() - email = '%s@%s.%s' % (username, random_string(), random_string(2)) + email = "%s@%s.%s" % (username, random_string(), random_string(2)) # Dunno why random tld doesn't work, but I'm too lazy now to investigate - openid = 'http://%s.%s.%s/' % (username, random_string(), 'org') + openid = "http://%s.%s.%s/" % (username, random_string(), "org") def login(self): - ''' + """ Login as user - ''' + """ self.client.login(username=self.username, password=self.password) def setUp(self): - ''' + """ Prepare for tests. - Create user - ''' + """ self.user = User.objects.create_user( username=self.username, password=self.password, @@ -61,12 +52,12 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods """ Test check page """ - response = self.client.get(reverse('tools_check')) - self.assertEqual(response.status_code, 200, 'no 200 ok?') + response = self.client.get(reverse("tools_check")) + self.assertEqual(response.status_code, 200, "no 200 ok?") def test_check_domain(self): """ Test check domain page """ - response = self.client.get(reverse('tools_check_domain')) - self.assertEqual(response.status_code, 200, 'no 200 ok?') + response = self.client.get(reverse("tools_check_domain")) + self.assertEqual(response.status_code, 200, "no 200 ok?") diff --git a/ivatar/tools/urls.py b/ivatar/tools/urls.py index 1a13b43..0a0c155 100644 --- a/ivatar/tools/urls.py +++ b/ivatar/tools/urls.py @@ -1,12 +1,13 @@ -''' +# -*- coding: utf-8 -*- +""" ivatar/tools URL configuration -''' +""" from django.conf.urls import url -from . views import CheckView, CheckDomainView +from .views import CheckView, CheckDomainView urlpatterns = [ # pylint: disable=invalid-name - url('check/', CheckView.as_view(), name='tools_check'), - url('check_domain/', CheckDomainView.as_view(), name='tools_check_domain'), - url('check_domain$', CheckDomainView.as_view(), name='tools_check_domain'), + url("check/", CheckView.as_view(), name="tools_check"), + url("check_domain/", CheckDomainView.as_view(), name="tools_check_domain"), + url("check_domain$", CheckDomainView.as_view(), name="tools_check_domain"), ] diff --git a/ivatar/tools/views.py b/ivatar/tools/views.py index a2adf8d..8b6986a 100644 --- a/ivatar/tools/views.py +++ b/ivatar/tools/views.py @@ -1,6 +1,7 @@ -''' +# -*- coding: utf-8 -*- +""" View classes for ivatar/tools/ -''' +""" from socket import inet_ntop, AF_INET6 import hashlib import random @@ -16,49 +17,59 @@ from libravatar import SECURE_BASE_URL as LIBRAVATAR_SECURE_BASE_URL from libravatar import BASE_URL as LIBRAVATAR_BASE_URL from ivatar.settings import SECURE_BASE_URL, BASE_URL -from .forms import CheckDomainForm, CheckForm # pylint: disable=relative-beyond-top-level +from .forms import ( + CheckDomainForm, + CheckForm, +) # pylint: disable=relative-beyond-top-level class CheckDomainView(FormView): - ''' + """ View class for checking a domain - ''' - template_name = 'check_domain.html' + """ + + template_name = "check_domain.html" form_class = CheckDomainForm - success_url = reverse('tools_check_domain') + success_url = reverse("tools_check_domain") def form_valid(self, form): result = {} super().form_valid(form) - domain = form.cleaned_data['domain'] - result['avatar_server_http'] = lookup_avatar_server(domain, False) - if result['avatar_server_http']: - result['avatar_server_http_ipv4'] = lookup_ip_address( - result['avatar_server_http'], - False) - result['avatar_server_http_ipv6'] = lookup_ip_address( - result['avatar_server_http'], - True) - result['avatar_server_https'] = lookup_avatar_server(domain, True) - if result['avatar_server_https']: - result['avatar_server_https_ipv4'] = lookup_ip_address( - result['avatar_server_https'], - False) - result['avatar_server_https_ipv6'] = lookup_ip_address( - result['avatar_server_https'], - True) - return render(self.request, self.template_name, { - 'form': form, - 'result': result, - }) + domain = form.cleaned_data["domain"] + result["avatar_server_http"] = lookup_avatar_server(domain, False) + if result["avatar_server_http"]: + result["avatar_server_http_ipv4"] = lookup_ip_address( + result["avatar_server_http"], False + ) + result["avatar_server_http_ipv6"] = lookup_ip_address( + result["avatar_server_http"], True + ) + result["avatar_server_https"] = lookup_avatar_server(domain, True) + if result["avatar_server_https"]: + result["avatar_server_https_ipv4"] = lookup_ip_address( + result["avatar_server_https"], False + ) + result["avatar_server_https_ipv6"] = lookup_ip_address( + result["avatar_server_https"], True + ) + return render( + self.request, + self.template_name, + { + "form": form, + "result": result, + }, + ) + class CheckView(FormView): - ''' + """ View class for checking an e-mail or openid address - ''' - template_name = 'check.html' + """ + + template_name = "check.html" form_class = CheckForm - success_url = reverse('tools_check') + success_url = reverse("tools_check") def form_valid(self, form): mailurl = None @@ -73,82 +84,88 @@ class CheckView(FormView): super().form_valid(form) - if form.cleaned_data['default_url']: - default_url = form.cleaned_data['default_url'] - elif form.cleaned_data['default_opt'] and form.cleaned_data['default_opt'] != 'none': - default_url = form.cleaned_data['default_opt'] + if form.cleaned_data["default_url"]: + default_url = form.cleaned_data["default_url"] + elif ( + form.cleaned_data["default_opt"] + and form.cleaned_data["default_opt"] != "none" + ): + default_url = form.cleaned_data["default_opt"] else: default_url = None - if 'size' in form.cleaned_data: - size = form.cleaned_data['size'] - if form.cleaned_data['mail']: + if "size" in form.cleaned_data: + size = form.cleaned_data["size"] + if form.cleaned_data["mail"]: mailurl = libravatar_url( - email=form.cleaned_data['mail'], - size=size, - default=default_url) + email=form.cleaned_data["mail"], size=size, default=default_url + ) mailurl = mailurl.replace(LIBRAVATAR_BASE_URL, BASE_URL) mailurl_secure = libravatar_url( - email=form.cleaned_data['mail'], + email=form.cleaned_data["mail"], size=size, https=True, - default=default_url) + default=default_url, + ) mailurl_secure = mailurl_secure.replace( - LIBRAVATAR_SECURE_BASE_URL, - SECURE_BASE_URL) + LIBRAVATAR_SECURE_BASE_URL, SECURE_BASE_URL + ) mail_hash = parse_user_identity( - email=form.cleaned_data['mail'], - openid=None)[0] - hash_obj = hashlib.new('sha256') - hash_obj.update(form.cleaned_data['mail'].encode('utf-8')) + email=form.cleaned_data["mail"], openid=None + )[0] + hash_obj = hashlib.new("sha256") + hash_obj.update(form.cleaned_data["mail"].encode("utf-8")) mail_hash256 = hash_obj.hexdigest() - mailurl_secure_256 = mailurl_secure.replace( - mail_hash, - mail_hash256) - if form.cleaned_data['openid']: - if not form.cleaned_data['openid'].startswith('http://') and \ - not form.cleaned_data['openid'].startswith('https://'): - form.cleaned_data['openid'] = 'http://%s' % form.cleaned_data['openid'] + mailurl_secure_256 = mailurl_secure.replace(mail_hash, mail_hash256) + if form.cleaned_data["openid"]: + if not form.cleaned_data["openid"].startswith( + "http://" + ) and not form.cleaned_data["openid"].startswith("https://"): + form.cleaned_data["openid"] = "http://%s" % form.cleaned_data["openid"] openidurl = libravatar_url( - openid=form.cleaned_data['openid'], - size=size, - default=default_url) + openid=form.cleaned_data["openid"], size=size, default=default_url + ) openidurl = openidurl.replace(LIBRAVATAR_BASE_URL, BASE_URL) openidurl_secure = libravatar_url( - openid=form.cleaned_data['openid'], + openid=form.cleaned_data["openid"], size=size, https=True, - default=default_url) + default=default_url, + ) openidurl_secure = openidurl_secure.replace( - LIBRAVATAR_SECURE_BASE_URL, - SECURE_BASE_URL) + LIBRAVATAR_SECURE_BASE_URL, SECURE_BASE_URL + ) openid_hash = parse_user_identity( - openid=form.cleaned_data['openid'], - email=None)[0] + openid=form.cleaned_data["openid"], email=None + )[0] - return render(self.request, self.template_name, { - 'form': form, - 'mailurl': mailurl, - 'openidurl': openidurl, - 'mailurl_secure': mailurl_secure, - 'mailurl_secure_256': mailurl_secure_256, - 'openidurl_secure': openidurl_secure, - 'mail_hash': mail_hash, - 'mail_hash256': mail_hash256, - 'openid_hash': openid_hash, - 'size': size, - }) + return render( + self.request, + self.template_name, + { + "form": form, + "mailurl": mailurl, + "openidurl": openidurl, + "mailurl_secure": mailurl_secure, + "mailurl_secure_256": mailurl_secure_256, + "openidurl_secure": openidurl_secure, + "mail_hash": mail_hash, + "mail_hash256": mail_hash256, + "openid_hash": openid_hash, + "size": size, + }, + ) def lookup_avatar_server(domain, https): - ''' + """ Extract the avatar server from an SRV record in the DNS zone The SRV records should look like this: _avatars._tcp.example.com. IN SRV 0 0 80 avatars.example.com _avatars-sec._tcp.example.com. IN SRV 0 0 443 avatars.example.com - ''' + """ if domain and len(domain) > 60: domain = domain[:60] @@ -161,27 +178,35 @@ def lookup_avatar_server(domain, https): DNS.DiscoverNameServers() try: - dns_request = DNS.Request(name=service_name, qtype='SRV').req() + dns_request = DNS.Request(name=service_name, qtype="SRV").req() except DNS.DNSError as message: print("DNS Error: %s (%s)" % (message, domain)) return None - if dns_request.header['status'] == 'NXDOMAIN': + if dns_request.header["status"] == "NXDOMAIN": # Not an error, but no point in going any further return None - if dns_request.header['status'] != 'NOERROR': - print("DNS Error: status=%s (%s)" % (dns_request.header['status'], domain)) + if dns_request.header["status"] != "NOERROR": + print("DNS Error: status=%s (%s)" % (dns_request.header["status"], domain)) return None records = [] for answer in dns_request.answers: - if ('data' not in answer) or (not answer['data']) or \ - (not answer['typename']) or (answer['typename'] != 'SRV'): + if ( + ("data" not in answer) + or (not answer["data"]) + or (not answer["typename"]) + or (answer["typename"] != "SRV") + ): continue - record = {'priority': int(answer['data'][0]), 'weight': int(answer['data'][1]), - 'port': int(answer['data'][2]), 'target': answer['data'][3]} + record = { + "priority": int(answer["data"][0]), + "weight": int(answer["data"][1]), + "port": int(answer["data"][2]), + "target": answer["data"][3], + } records.append(record) @@ -194,38 +219,38 @@ def lookup_avatar_server(domain, https): def srv_hostname(records): - ''' + """ Return the right (target, port) pair from a list of SRV records. - ''' + """ if len(records) < 1: return (None, None) if len(records) == 1: ret = records[0] - return (ret['target'], ret['port']) + return (ret["target"], ret["port"]) # Keep only the servers in the top priority priority_records = [] total_weight = 0 - top_priority = records[0]['priority'] # highest priority = lowest number + top_priority = records[0]["priority"] # highest priority = lowest number for ret in records: - if ret['priority'] > top_priority: + if ret["priority"] > top_priority: # ignore the record (ret has lower priority) continue # Take care - this if is only a if, if the above if # uses continue at the end. else it should be an elsif - if ret['priority'] < top_priority: + if ret["priority"] < top_priority: # reset the aretay (ret has higher priority) - top_priority = ret['priority'] + top_priority = ret["priority"] total_weight = 0 priority_records = [] - total_weight += ret['weight'] + total_weight += ret["weight"] - if ret['weight'] > 0: + if ret["weight"] > 0: priority_records.append((total_weight, ret)) else: # zero-weigth elements must come first @@ -233,7 +258,7 @@ def srv_hostname(records): if len(priority_records) == 1: unused, ret = priority_records[0] # pylint: disable=unused-variable - return (ret['target'], ret['port']) + return (ret["target"], ret["port"]) # Select first record according to RFC2782 weight ordering algorithm (page 3) random_number = random.randint(0, total_weight) @@ -242,9 +267,9 @@ def srv_hostname(records): weighted_index, ret = record if weighted_index >= random_number: - return (ret['target'], ret['port']) + return (ret["target"], ret["port"]) - print('There is something wrong with our SRV weight ordering algorithm') + print("There is something wrong with our SRV weight ordering algorithm") return (None, None) @@ -263,19 +288,21 @@ def lookup_ip_address(hostname, ipv6): print("DNS Error: %s (%s)" % (message, hostname)) return None - if dns_request.header['status'] != 'NOERROR': - print("DNS Error: status=%s (%s)" % (dns_request.header['status'], hostname)) + if dns_request.header["status"] != "NOERROR": + print("DNS Error: status=%s (%s)" % (dns_request.header["status"], hostname)) return None for answer in dns_request.answers: - if ('data' not in answer) or (not answer['data']): + if ("data" not in answer) or (not answer["data"]): continue - if (ipv6 and answer['typename'] != 'AAAA') or (not ipv6 and answer['typename'] != 'A'): + if (ipv6 and answer["typename"] != "AAAA") or ( + not ipv6 and answer["typename"] != "A" + ): continue # skip CNAME records if ipv6: - return inet_ntop(AF_INET6, answer['data']) + return inet_ntop(AF_INET6, answer["data"]) - return answer['data'] + return answer["data"] return None diff --git a/ivatar/urls.py b/ivatar/urls.py index 96b4d22..b76798e 100644 --- a/ivatar/urls.py +++ b/ivatar/urls.py @@ -1,42 +1,58 @@ -''' +# -*- coding: utf-8 -*- +""" ivatar URL configuration -''' +""" from django.contrib import admin from django.urls import path, include from django.conf.urls import url from django.conf.urls.static import static from django.views.generic import TemplateView, RedirectView from ivatar import settings -from . views import AvatarImageView, GravatarProxyView, StatsView +from .views import AvatarImageView, GravatarProxyView, StatsView urlpatterns = [ # pylint: disable=invalid-name - path('admin/', admin.site.urls), - path('i18n/', include('django.conf.urls.i18n')), - url('openid/', include('django_openid_auth.urls')), - url('tools/', include('ivatar.tools.urls')), + path("admin/", admin.site.urls), + path("i18n/", include("django.conf.urls.i18n")), + url("openid/", include("django_openid_auth.urls")), + url("tools/", include("ivatar.tools.urls")), + url(r"avatar/(?P\w{64})", AvatarImageView.as_view(), name="avatar_view"), + url(r"avatar/(?P\w{32})", AvatarImageView.as_view(), name="avatar_view"), + url(r"avatar/$", AvatarImageView.as_view(), name="avatar_view"), url( - r'avatar/(?P\w{64})', - AvatarImageView.as_view(), name='avatar_view'), + r"avatar/(?P\w*)", + RedirectView.as_view(url="/static/img/deadbeef.png"), + name="invalid_hash", + ), url( - r'avatar/(?P\w{32})', - AvatarImageView.as_view(), name='avatar_view'), - url(r'avatar/$', AvatarImageView.as_view(), name='avatar_view'), + r"gravatarproxy/(?P\w*)", + GravatarProxyView.as_view(), + name="gravatarproxy", + ), url( - r'avatar/(?P\w*)', - RedirectView.as_view(url='/static/img/deadbeef.png'), name='invalid_hash'), - url( - r'gravatarproxy/(?P\w*)', - GravatarProxyView.as_view(), name='gravatarproxy'), - url('description/', TemplateView.as_view(template_name='description.html'), name='description'), + "description/", + TemplateView.as_view(template_name="description.html"), + name="description", + ), # The following two are TODO TODO TODO TODO TODO - url('run_your_own/', - TemplateView.as_view(template_name='run_your_own.html'), name='run_your_own'), - url('features/', TemplateView.as_view(template_name='features.html'), name='features'), - url('security/', TemplateView.as_view(template_name='security.html'), name='security'), - url('privacy/', TemplateView.as_view(template_name='privacy.html'), name='privacy'), - url('contact/', TemplateView.as_view(template_name='contact.html'), name='contact'), - path('talk_to_us/', RedirectView.as_view(url='/contact'), name='talk_to_us'), - url('stats/', StatsView.as_view(), name='stats'), + url( + "run_your_own/", + TemplateView.as_view(template_name="run_your_own.html"), + name="run_your_own", + ), + url( + "features/", + TemplateView.as_view(template_name="features.html"), + name="features", + ), + url( + "security/", + TemplateView.as_view(template_name="security.html"), + name="security", + ), + url("privacy/", TemplateView.as_view(template_name="privacy.html"), name="privacy"), + url("contact/", TemplateView.as_view(template_name="contact.html"), name="contact"), + path("talk_to_us/", RedirectView.as_view(url="/contact"), name="talk_to_us"), + url("stats/", StatsView.as_view(), name="stats"), ] MAINTENANCE = False @@ -47,11 +63,15 @@ except: # pylint: disable=bare-except pass if MAINTENANCE: - urlpatterns.append(url('', TemplateView.as_view(template_name='maintenance.html'), name='home')) - urlpatterns.insert(3, url('accounts/', RedirectView.as_view(url='/'))) + urlpatterns.append( + url("", TemplateView.as_view(template_name="maintenance.html"), name="home") + ) + urlpatterns.insert(3, url("accounts/", RedirectView.as_view(url="/"))) else: - urlpatterns.append(url('', TemplateView.as_view(template_name='home.html'), name='home')) - urlpatterns.insert(3, url('accounts/', include('ivatar.ivataraccount.urls'))) + urlpatterns.append( + url("", TemplateView.as_view(template_name="home.html"), name="home") + ) + urlpatterns.insert(3, url("accounts/", include("ivatar.ivataraccount.urls"))) urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/ivatar/utils.py b/ivatar/utils.py index 3ed3f03..280dba3 100644 --- a/ivatar/utils.py +++ b/ivatar/utils.py @@ -1,59 +1,65 @@ -''' +# -*- coding: utf-8 -*- +""" Simple module providing reusable random_string function -''' +""" import random import string from PIL import Image, ImageDraw def random_string(length=10): - ''' + """ Return some random string with default length 10 - ''' - return ''.join(random.SystemRandom().choice( - string.ascii_lowercase + string.digits) for _ in range(length)) + """ + return "".join( + random.SystemRandom().choice(string.ascii_lowercase + string.digits) + for _ in range(length) + ) def openid_variations(openid): - ''' + """ Return the various OpenID variations, ALWAYS in the same order: - http w/ trailing slash - http w/o trailing slash - https w/ trailing slash - https w/o trailing slash - ''' + """ # Make the 'base' version: http w/ trailing slash - if openid.startswith('https://'): - openid = openid.replace('https://', 'http://') - if openid[-1] != '/': - openid = openid + '/' + if openid.startswith("https://"): + openid = openid.replace("https://", "http://") + if openid[-1] != "/": + openid = openid + "/" # http w/o trailing slash var1 = openid[0:-1] - var2 = openid.replace('http://', 'https://') + var2 = openid.replace("http://", "https://") var3 = var2[0:-1] return (openid, var1, var2, var3) -def mm_ng(idhash, size=80, add_red=0, add_green=0, add_blue=0): #pylint: disable=too-many-locals - ''' + +def mm_ng( + idhash, size=80, add_red=0, add_green=0, add_blue=0 +): # pylint: disable=too-many-locals + """ Return an MM (mystery man) image, based on a given hash add some red, green or blue, if specified - ''' + """ # Make sure the lightest bg color we paint is e0, else # we do not see the MM any more - if idhash[0] == 'f': - idhash = 'e0' + if idhash[0] == "f": + idhash = "e0" # How large is the circle? - circlesize = size*0.6 + circlesize = size * 0.6 # Coordinates for the circle - start_x = int(size*0.2) - end_x = start_x+circlesize - start_y = int(size*0.05) - end_y = start_y+circlesize + start_x = int(size * 0.2) + end_x = start_x + circlesize + start_y = int(size * 0.05) + end_y = start_y + circlesize # All are the same, based on the input hash # this should always result in a "gray-ish" background @@ -62,44 +68,47 @@ def mm_ng(idhash, size=80, add_red=0, add_green=0, add_blue=0): #pylint: disabl blue = idhash[0:2] # Add some red (i/a) and make sure it's not over 255 - red = hex(int(red, 16)+add_red).replace('0x', '') + red = hex(int(red, 16) + add_red).replace("0x", "") if int(red, 16) > 255: - red = 'ff' + red = "ff" if len(red) == 1: - red = '0%s' % red + red = "0%s" % red # Add some green (i/a) and make sure it's not over 255 - green = hex(int(green, 16)+add_green).replace('0x', '') + green = hex(int(green, 16) + add_green).replace("0x", "") if int(green, 16) > 255: - green = 'ff' + green = "ff" if len(green) == 1: - green = '0%s' % green + green = "0%s" % green # Add some blue (i/a) and make sure it's not over 255 - blue = hex(int(blue, 16)+add_blue).replace('0x', '') + blue = hex(int(blue, 16) + add_blue).replace("0x", "") if int(blue, 16) > 255: - blue = 'ff' + blue = "ff" if len(blue) == 1: - blue = '0%s' % blue + blue = "0%s" % blue # Assemable the bg color "string" in webnotation. Eg. '#d3d3d3' - bg_color = '#' + red + green + blue + bg_color = "#" + red + green + blue # Image - image = Image.new('RGB', (size, size)) + image = Image.new("RGB", (size, size)) draw = ImageDraw.Draw(image) # Draw background draw.rectangle(((0, 0), (size, size)), fill=bg_color) # Draw MMs head - draw.ellipse((start_x, start_y, end_x, end_y), fill='white') + draw.ellipse((start_x, start_y, end_x, end_y), fill="white") # Draw MMs 'body' - draw.polygon(( - (start_x+circlesize/2, size/2.5), - (size*0.15, size), - (size-size*0.15, size)), - fill='white') + draw.polygon( + ( + (start_x + circlesize / 2, size / 2.5), + (size * 0.15, size), + (size - size * 0.15, size), + ), + fill="white", + ) return image diff --git a/ivatar/views.py b/ivatar/views.py index d1dcb4d..2ec933c 100644 --- a/ivatar/views.py +++ b/ivatar/views.py @@ -13,7 +13,7 @@ from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponseNotFound, JsonResponse from django.core.exceptions import ObjectDoesNotExist from django.core.cache import cache, caches -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.urls import reverse_lazy from django.db.models import Q from django.contrib.auth.models import User diff --git a/libravatarproxy.py b/libravatarproxy.py index eb19fc8..c9dcca6 100755 --- a/libravatarproxy.py +++ b/libravatarproxy.py @@ -1,21 +1,26 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- import urllib.request import sys import os -sys.stderr.buffer.write(b'%s' % bytes(os.environ.get("QUERY_STRING", "No Query String in url"), 'utf-8')) +sys.stderr.buffer.write( + b"%s" % bytes(os.environ.get("QUERY_STRING", "No Query String in url"), "utf-8") +) -link = 'https://www.libravatar.org/avatar/%s' % os.environ.get("QUERY_STRING", 'x'*32) -sys.stderr.buffer.write(b'%s' % bytes(link, 'utf-8')) +link = "https://www.libravatar.org/avatar/%s" % os.environ.get("QUERY_STRING", "x" * 32) +sys.stderr.buffer.write(b"%s" % bytes(link, "utf-8")) data = None with urllib.request.urlopen(link) as f: data = f.read() for header in f.headers._headers: - if header[0] == 'Content-Type': - sys.stdout.buffer.write(b"%s: %s\n\n" % (bytes(header[0], 'utf-8'), bytes(header[1], 'utf-8'))) + if header[0] == "Content-Type": + sys.stdout.buffer.write( + b"%s: %s\n\n" % (bytes(header[0], "utf-8"), bytes(header[1], "utf-8")) + ) sys.stdout.flush() break diff --git a/requirements.txt b/requirements.txt index cb3c621..f385a08 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ autopep8 bcrypt defusedxml -Django < 4 +Django < 4.0 django-anymail[mailgun] django-auth-ldap django-bootstrap4