WIP: Happy lint

This commit is contained in:
Oliver Falk
2018-06-19 15:43:09 +02:00
parent ed0e720c2b
commit 32eb5afc26
13 changed files with 147 additions and 106 deletions

View File

@@ -2,8 +2,8 @@
Default: useful variables for the base page templates. Default: useful variables for the base page templates.
''' '''
from ivatar.settings import IVATAR_VERSION, SITE_NAME
from ipware import get_client_ip from ipware import get_client_ip
from ivatar.settings import IVATAR_VERSION, SITE_NAME
def basepage(request): def basepage(request):
@@ -15,7 +15,7 @@ def basepage(request):
if 'openid_identifier' in request.GET: if 'openid_identifier' in request.GET:
context['openid_identifier'] = \ context['openid_identifier'] = \
request.GET['openid_identifier'] # pragma: no cover request.GET['openid_identifier'] # pragma: no cover
client_ip, is_routable = get_client_ip(request) client_ip = get_client_ip(request)[0]
context['client_ip'] = client_ip context['client_ip'] = client_ip
context['ivatar_version'] = IVATAR_VERSION context['ivatar_version'] = IVATAR_VERSION
context['site_name'] = SITE_NAME context['site_name'] = SITE_NAME

View File

@@ -1,3 +1,6 @@
'''
Register models in admin
'''
from django.contrib import admin from django.contrib import admin
from . models import Photo, ConfirmedEmail, UnconfirmedEmail from . models import Photo, ConfirmedEmail, UnconfirmedEmail

View File

@@ -1,19 +1,21 @@
'''
Classes for our ivatar.ivataraccount.forms
'''
from urllib.parse import urlsplit, urlunsplit
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.urls import reverse from django.urls import reverse
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.core.mail import send_mail from django.core.mail import send_mail
from urllib.parse import urlsplit, urlunsplit from ipware import get_client_ip
from ivatar import settings from ivatar import settings
from . models import UnconfirmedEmail, ConfirmedEmail, Photo
from . models import UnconfirmedOpenId, ConfirmedOpenId
from ivatar.settings import MAX_LENGTH_EMAIL from ivatar.settings import MAX_LENGTH_EMAIL
from ivatar.ivataraccount.models import MAX_LENGTH_URL from ivatar.ivataraccount.models import MAX_LENGTH_URL
from . models import UnconfirmedEmail, ConfirmedEmail, Photo
from ipware import get_client_ip from . models import UnconfirmedOpenId, ConfirmedOpenId
MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT = 5 MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT = 5
@@ -54,7 +56,7 @@ class AddEmailForm(forms.Form):
# Check whether or not a confirmation email has been # Check whether or not a confirmation email has been
# sent by this user already # sent by this user already
if UnconfirmedEmail.objects.filter( if UnconfirmedEmail.objects.filter( # pylint: disable=no-member
user=user, email=self.cleaned_data['email']).exists(): user=user, email=self.cleaned_data['email']).exists():
self.add_error('email', _('Address already added, currently unconfirmed')) self.add_error('email', _('Address already added, currently unconfirmed'))
return False return False
@@ -112,7 +114,8 @@ class UploadPhotoForm(forms.Form):
distribute photos to third parties.') distribute photos to third parties.')
}) })
def save(self, request, data): @staticmethod
def save(request, data):
''' '''
Save the model and assign it to the current user Save the model and assign it to the current user
''' '''
@@ -122,7 +125,7 @@ class UploadPhotoForm(forms.Form):
photo.ip_address = get_client_ip(request) photo.ip_address = get_client_ip(request)
photo.data = data.read() photo.data = data.read()
photo.save() photo.save()
if not photo.id: if not photo.pk:
return None return None
return photo return photo
@@ -151,19 +154,18 @@ class AddOpenIDForm(forms.Form):
url.query, url.fragment)) url.query, url.fragment))
# TODO: Domain restriction as in libravatar? # TODO: Domain restriction as in libravatar?
return data return data
def save(self, user): def save(self, user):
''' '''
Save the model, ensuring some safety Save the model, ensuring some safety
''' '''
if ConfirmedOpenId.objects.filter( if ConfirmedOpenId.objects.filter( # pylint: disable=no-member
openid=self.cleaned_data['openid']).exists(): openid=self.cleaned_data['openid']).exists():
self.add_error('openid', _('OpenID already added and confirmed!')) self.add_error('openid', _('OpenID already added and confirmed!'))
return False return False
if UnconfirmedOpenId.objects.filter( if UnconfirmedOpenId.objects.filter( # pylint: disable=no-member
openid=self.cleaned_data['openid']).exists(): openid=self.cleaned_data['openid']).exists():
self.add_error('openid', _('OpenID already added, but not confirmed yet!')) self.add_error('openid', _('OpenID already added, but not confirmed yet!'))
return False return False
@@ -173,4 +175,4 @@ class AddOpenIDForm(forms.Form):
unconfirmed.user = user unconfirmed.user = user
unconfirmed.save() unconfirmed.save()
return unconfirmed.id return unconfirmed.pk

View File

@@ -1,3 +1,6 @@
'''
Helper method to fetch Gravatar image
'''
from ssl import SSLError from ssl import SSLError
from urllib.request import urlopen, HTTPError, URLError from urllib.request import urlopen, HTTPError, URLError
import hashlib import hashlib
@@ -21,18 +24,18 @@ def get_photo(email):
try: try:
urlopen(image_url, timeout=URL_TIMEOUT) urlopen(image_url, timeout=URL_TIMEOUT)
except HTTPError as e: except HTTPError as e: # pylint: disable=invalid-name
if e.code != 404 and e.code != 503: if e.code != 404 and e.code != 503:
print( # pragma: no cover print( # pragma: no cover
'Gravatar fetch failed with an unexpected %s HTTP error' % 'Gravatar fetch failed with an unexpected %s HTTP error' %
e.code) e.code)
return False return False
except URLError as e: # pragma: no cover except URLError as e: # pragma: no cover # pylint: disable=invalid-name
print( print(
'Gravatar fetch failed with URL error: %s' % 'Gravatar fetch failed with URL error: %s' %
e.reason) # pragma: no cover e.reason) # pragma: no cover
return False # pragma: no cover return False # pragma: no cover
except SSLError as e: # pragma: no cover except SSLError as e: # pragma: no cover # pylint: disable=invalid-name
print( print(
'Gravatar fetch failed with SSL error: %s' % 'Gravatar fetch failed with SSL error: %s' %
e.reason) # pragma: no cover e.reason) # pragma: no cover

View File

@@ -19,10 +19,10 @@ from django.utils import timezone
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ObjectDoesNotExist
from openid.association import Association as OIDAssociation from openid.association import Association as OIDAssociation
from openid.store import nonce as oidnonce from openid.store import nonce as oidnonce
from openid.store.interface import OpenIDStore from openid.store.interface import OpenIDStore
from ipware import get_client_ip
from ivatar.settings import MAX_LENGTH_EMAIL, logger from ivatar.settings import MAX_LENGTH_EMAIL, logger
from ivatar.settings import MAX_PIXELS, AVATAR_MAX_SIZE, JPEG_QUALITY from ivatar.settings import MAX_PIXELS, AVATAR_MAX_SIZE, JPEG_QUALITY
@@ -42,6 +42,7 @@ def file_format(image_type):
return 'png' return 'png'
elif image_type == 'GIF': elif image_type == 'GIF':
return 'gif' return 'gif'
return None
def pil_format(image_type): def pil_format(image_type):
''' '''
@@ -54,7 +55,7 @@ def pil_format(image_type):
elif image_type == 'gif': elif image_type == 'gif':
return 'GIF' return 'GIF'
logger.info('Unsupported file format: %s' % image_type) logger.info('Unsupported file format: %s', image_type)
return None return None
@@ -69,7 +70,7 @@ class BaseAccountModel(models.Model):
ip_address = models.GenericIPAddressField(unpack_ipv4=True, null=True) ip_address = models.GenericIPAddressField(unpack_ipv4=True, null=True)
add_date = models.DateTimeField(default=timezone.now) add_date = models.DateTimeField(default=timezone.now)
class Meta: class Meta: # pylint: disable=too-few-public-methods
''' '''
Class attributes Class attributes
''' '''
@@ -84,7 +85,7 @@ class Photo(BaseAccountModel):
data = models.BinaryField() data = models.BinaryField()
format = models.CharField(max_length=3) format = models.CharField(max_length=3)
class Meta: class Meta: # pylint: disable=too-few-public-methods
''' '''
Class attributes Class attributes
''' '''
@@ -92,6 +93,9 @@ class Photo(BaseAccountModel):
verbose_name_plural = _('photos') verbose_name_plural = _('photos')
def import_image(self, service_name, email_address): def import_image(self, service_name, email_address):
'''
Allow to import image from other (eg. Gravatar) service
'''
image_url = False image_url = False
if service_name == 'Gravatar': if service_name == 'Gravatar':
@@ -104,12 +108,12 @@ class Photo(BaseAccountModel):
try: try:
image = urlopen(image_url) image = urlopen(image_url)
# No idea how to test this # No idea how to test this
except HTTPError as e: # pragma: no cover except HTTPError as e: # pragma: no cover # pylint: disable=invalid-name
print('%s import failed with an HTTP error: %s' % print('%s import failed with an HTTP error: %s' %
(service_name, e.code)) (service_name, e.code))
return False return False
# No idea how to test this # No idea how to test this
except URLError as e: # pragma: no cover except URLError as e: # pragma: no cover # pylint: disable=invalid-name
print('%s import failed: %s' % (service_name, e.reason)) print('%s import failed: %s' % (service_name, e.reason))
return False return False
data = image.read() data = image.read()
@@ -128,7 +132,8 @@ class Photo(BaseAccountModel):
super().save() super().save()
return True return True
def save(self, *args, **kwargs): def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
''' '''
Override save from parent, taking care about the image Override save from parent, taking care about the image
''' '''
@@ -136,7 +141,7 @@ class Photo(BaseAccountModel):
try: try:
img = Image.open(BytesIO(self.data)) img = Image.open(BytesIO(self.data))
# Testing? Ideas anyone? # Testing? Ideas anyone?
except Exception as e: # pylint: disable=unused-variable except Exception as e: # pylint: disable=invalid-name,broad-except
# For debugging only # For debugging only
print('Exception caught: %s' % e) print('Exception caught: %s' % e)
return False return False
@@ -144,18 +149,21 @@ class Photo(BaseAccountModel):
if not self.format: if not self.format:
print('Format not recognized') print('Format not recognized')
return False return False
return super().save(*args, **kwargs) return super().save(force_insert, force_update, using, update_fields)
def perform_crop(self, request, dimensions, email, openid): def perform_crop(self, request, dimensions, email, openid):
'''
Helper to crop the image
'''
if request.user.photo_set.count() == 1: if request.user.photo_set.count() == 1:
# This is the first photo, assign to all confirmed addresses # This is the first photo, assign to all confirmed addresses
for email in request.user.confirmedemail_set.all(): for addr in request.user.confirmedemail_set.all():
email.photo = self addr.photo = self
email.save() addr.save()
for openid in request.user.confirmedopenid_set.all(): for addr in request.user.confirmedopenid_set.all():
openid.photo = self addr.photo = self
openid.save() addr.save()
if email: if email:
# Explicitely asked # Explicitely asked
@@ -171,25 +179,30 @@ class Photo(BaseAccountModel):
img = Image.open(BytesIO(self.data)) img = Image.open(BytesIO(self.data))
# This should be anyway checked during save... # This should be anyway checked during save...
a, b = img.size dimensions['a'], dimensions['b'] = img.size # pylint: disable=invalid-name
if a > MAX_PIXELS or b > MAX_PIXELS: if dimensions['a'] > MAX_PIXELS or dimensions['b'] > MAX_PIXELS:
messages.error(request, _('Image dimensions are too big(max: %s x %s' % (MAX_PIXELS, MAX_PIXELS))) messages.error(
request,
_('Image dimensions are too big(max: %s x %s' %
(MAX_PIXELS, MAX_PIXELS)))
return HttpResponseRedirect(reverse_lazy('profile')) return HttpResponseRedirect(reverse_lazy('profile'))
w = dimensions['w'] if dimensions['w'] == 0 and dimensions['h'] == 0:
h = dimensions['h'] dimensions['w'], dimensions['h'] = dimensions['a'], dimensions['b']
x = dimensions['x'] min_from_w_h = min(dimensions['w'], dimensions['h'])
y = dimensions['y'] dimensions['w'], dimensions['h'] = min_from_w_h, min_from_w_h
elif dimensions['w'] < 0 or \
if w == 0 and h == 0: (dimensions['x'] + dimensions['w']) > dimensions['a'] or \
w, h = a, b dimensions['h'] < 0 or \
i = min(w, h) (dimensions['y'] + dimensions['h']) > dimensions['b']:
w, h = i, i
elif w < 0 or (x + w) > a or h < 0 or (y + h) > b:
messages.error(request, _('Crop outside of original image bounding box')) messages.error(request, _('Crop outside of original image bounding box'))
return HttpResponseRedirect(reverse_lazy('profile')) return HttpResponseRedirect(reverse_lazy('profile'))
cropped = img.crop((x, y, x + w, y + h)) cropped = img.crop((
dimensions['x'],
dimensions['y'],
dimensions['x'] + dimensions['w'],
dimensions['y'] + dimensions['h']))
# cropped.load() # cropped.load()
# Resize the image only if it's larger than the specified max width. # Resize the image only if it's larger than the specified max width.
cropped_w, cropped_h = cropped.size cropped_w, cropped_h = cropped.size
@@ -200,12 +213,6 @@ class Photo(BaseAccountModel):
data = BytesIO() data = BytesIO()
cropped.save(data, pil_format(self.format), quality=JPEG_QUALITY) cropped.save(data, pil_format(self.format), quality=JPEG_QUALITY)
data.seek(0) data.seek(0)
# Create new photo?
# photo = Photo()
# photo.user = request.user
# photo.ip_address = get_client_ip(request)
# photo.data = data.read()
# photo.save()
# Overwrite the existing image # Overwrite the existing image
self.data = data.read() self.data = data.read()
@@ -214,12 +221,13 @@ class Photo(BaseAccountModel):
return HttpResponseRedirect(reverse_lazy('profile')) return HttpResponseRedirect(reverse_lazy('profile'))
class ConfirmedEmailManager(models.Manager): class ConfirmedEmailManager(models.Manager): # pylint: disable=too-few-public-methods
''' '''
Manager for our confirmed email addresses model Manager for our confirmed email addresses model
''' '''
def create_confirmed_email(self, user, email_address, is_logged_in): @staticmethod
def create_confirmed_email(user, email_address, is_logged_in):
''' '''
Helper method to create confirmed email address Helper method to create confirmed email address
''' '''
@@ -254,7 +262,7 @@ class ConfirmedEmail(BaseAccountModel):
digest = models.CharField(max_length=64) digest = models.CharField(max_length=64)
objects = ConfirmedEmailManager() objects = ConfirmedEmailManager()
class Meta: class Meta: # pylint: disable=too-few-public-methods
''' '''
Class attributes Class attributes
''' '''
@@ -268,14 +276,15 @@ class ConfirmedEmail(BaseAccountModel):
self.photo = photo self.photo = photo
self.save() self.save()
def save(self, *args, **kwargs): def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
''' '''
Override save from parent, add digest Override save from parent, add digest
''' '''
self.digest = hashlib.md5( self.digest = hashlib.md5(
self.email.strip().lower().encode('utf-8') self.email.strip().lower().encode('utf-8')
).hexdigest() ).hexdigest()
return super().save(*args, **kwargs) return super().save(force_insert, force_update, using, update_fields)
class UnconfirmedEmail(BaseAccountModel): class UnconfirmedEmail(BaseAccountModel):
@@ -285,18 +294,19 @@ class UnconfirmedEmail(BaseAccountModel):
email = models.EmailField(max_length=MAX_LENGTH_EMAIL) email = models.EmailField(max_length=MAX_LENGTH_EMAIL)
verification_key = models.CharField(max_length=64) verification_key = models.CharField(max_length=64)
class Meta: class Meta: # pylint: disable=too-few-public-methods
''' '''
Class attributes Class attributes
''' '''
verbose_name = _('unconfirmed_email') verbose_name = _('unconfirmed_email')
verbose_name_plural = _('unconfirmed_emails') verbose_name_plural = _('unconfirmed_emails')
def save(self, *args, **kwargs): def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
hash_object = hashlib.new('sha256') hash_object = hashlib.new('sha256')
hash_object.update(urandom(1024) + self.user.username.encode('utf-8')) hash_object.update(urandom(1024) + self.user.username.encode('utf-8')) # pylint: disable=no-member
self.verification_key = hash_object.hexdigest() self.verification_key = hash_object.hexdigest()
super(UnconfirmedEmail, self).save(*args, **kwargs) super(UnconfirmedEmail, self).save(force_insert, force_update, using, update_fields)
class UnconfirmedOpenId(BaseAccountModel): class UnconfirmedOpenId(BaseAccountModel):
@@ -305,7 +315,10 @@ class UnconfirmedOpenId(BaseAccountModel):
''' '''
openid = models.URLField(unique=False, max_length=MAX_LENGTH_URL) openid = models.URLField(unique=False, max_length=MAX_LENGTH_URL)
class Meta: class Meta: # pylint: disable=too-few-public-methods
'''
Meta class
'''
verbose_name = _('unconfirmed OpenID') verbose_name = _('unconfirmed OpenID')
verbose_name_plural = ('unconfirmed_OpenIDs') verbose_name_plural = ('unconfirmed_OpenIDs')
@@ -325,15 +338,22 @@ class ConfirmedOpenId(BaseAccountModel):
) )
digest = models.CharField(max_length=64) digest = models.CharField(max_length=64)
class Meta: class Meta: # pylint: disable=too-few-public-methods
'''
Meta class
'''
verbose_name = _('confirmed OpenID') verbose_name = _('confirmed OpenID')
verbose_name_plural = _('confirmed OpenIDs') verbose_name_plural = _('confirmed OpenIDs')
def set_photo(self, photo): def set_photo(self, photo):
'''
Helper method to save photo
'''
self.photo = photo self.photo = photo
self.save() self.save()
def save(self, *args, **kwargs): def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
url = urlsplit(self.openid) url = urlsplit(self.openid)
if url.username: # pragma: no cover if url.username: # pragma: no cover
password = url.password or '' password = url.password or ''
@@ -344,7 +364,7 @@ class ConfirmedOpenId(BaseAccountModel):
(url.scheme.lower(), netloc, url.path, url.query, url.fragment) (url.scheme.lower(), netloc, url.path, url.query, url.fragment)
) )
self.digest = hashlib.sha256(lowercase_url.encode('utf-8')).hexdigest() self.digest = hashlib.sha256(lowercase_url.encode('utf-8')).hexdigest()
return super().save(*args, **kwargs) return super().save(force_insert, force_update, using, update_fields)
class OpenIDNonce(models.Model): class OpenIDNonce(models.Model):
@@ -383,7 +403,7 @@ class DjangoOpenIDStore(OpenIDStore):
assoc = OpenIDAssociation( assoc = OpenIDAssociation(
server_url=server_url, server_url=server_url,
handle=association.handle, handle=association.handle,
secret=base64.encodestring(association.secret), secret=base64.encodebytes(association.secret),
issued=association.issued, issued=association.issued,
lifetime=association.issued, lifetime=association.issued,
assoc_type=association.assoc_type) assoc_type=association.assoc_type)
@@ -395,25 +415,25 @@ class DjangoOpenIDStore(OpenIDStore):
''' '''
assocs = [] assocs = []
if handle is not None: if handle is not None:
assocs = OpenIDAssociation.objects.filter( assocs = OpenIDAssociation.objects.filter( # pylint: disable=no-member
server_url=server_url, handle=handle) server_url=server_url, handle=handle)
else: else:
assocs = OpenIDAssociation.objects.filter(server_url=server_url) assocs = OpenIDAssociation.objects.filter(server_url=server_url) # pylint: disable=no-member
if not assocs: if not assocs:
return None return None
associations = [] associations = []
for assoc in assocs: for assoc in assocs:
if type(assoc.secret) is str: if isinstance(assoc.secret, str):
assoc.secret = assoc.secret.split("b'")[1].split("'")[0] assoc.secret = assoc.secret.split("b'")[1].split("'")[0]
assoc.secret = bytes(assoc.secret, 'utf-8') assoc.secret = bytes(assoc.secret, 'utf-8')
association = OIDAssociation(assoc.handle, association = OIDAssociation(assoc.handle,
base64.decodestring(assoc.secret), base64.decodebytes(assoc.secret),
assoc.issued, assoc.lifetime, assoc.issued, assoc.lifetime,
assoc.assoc_type) assoc.assoc_type)
expires = 0 expires = 0
try: try:
expires = association.getExpiresIn() expires = association.getExpiresIn() # pylint: disable=no-member
except Exception as e: except Exception as e: # pylint: disable=invalid-name,broad-except,unused-variable
expires = association.expiresIn expires = association.expiresIn
if expires == 0: if expires == 0:
self.removeAssociation(server_url, assoc.handle) self.removeAssociation(server_url, assoc.handle)
@@ -429,7 +449,7 @@ class DjangoOpenIDStore(OpenIDStore):
TODO: Could be moved to classmethod TODO: Could be moved to classmethod
''' '''
assocs = list( assocs = list(
OpenIDAssociation.objects.filter( OpenIDAssociation.objects.filter( # pylint: disable=no-member
server_url=server_url, handle=handle)) server_url=server_url, handle=handle))
assocs_exist = len(assocs) > 0 assocs_exist = len(assocs) > 0
for assoc in assocs: for assoc in assocs:
@@ -445,12 +465,12 @@ class DjangoOpenIDStore(OpenIDStore):
if abs(timestamp - time.time()) > oidnonce.SKEW: if abs(timestamp - time.time()) > oidnonce.SKEW:
return False return False
try: try:
nonce = OpenIDNonce.objects.get( nonce = OpenIDNonce.objects.get( # pylint: disable=no-member
server_url__exact=server_url, server_url__exact=server_url,
timestamp__exact=timestamp, timestamp__exact=timestamp,
salt__exact=salt) salt__exact=salt)
except OpenIDNonce.DoesNotExist: except ObjectDoesNotExist:
nonce = OpenIDNonce.objects.create( nonce = OpenIDNonce.objects.create( # pylint: disable=no-member
server_url=server_url, timestamp=timestamp, salt=salt) server_url=server_url, timestamp=timestamp, salt=salt)
return True return True
nonce.delete() nonce.delete()
@@ -462,12 +482,12 @@ class DjangoOpenIDStore(OpenIDStore):
TODO: Could be moved to classmethod TODO: Could be moved to classmethod
''' '''
timestamp = int(time.time()) - oidnonce.SKEW timestamp = int(time.time()) - oidnonce.SKEW
OpenIDNonce.objects.filter(timestamp__lt=timestamp).delete() OpenIDNonce.objects.filter(timestamp__lt=timestamp).delete() # pylint: disable=no-member
def cleanupAssociations(self): # pragma: no cover def cleanupAssociations(self): # pragma: no cover
''' '''
Helper method to cleanup associations Helper method to cleanup associations
TODO: Could be moved to classmethod TODO: Could be moved to classmethod
''' '''
OpenIDAssociation.objects.extra( OpenIDAssociation.objects.extra( # pylint: disable=no-member
where=['issued + lifetimeint < (%s)' % time.time()]).delete() where=['issued + lifetimeint < (%s)' % time.time()]).delete()

View File

@@ -17,7 +17,7 @@ from . views import CropPhotoView
# Define URL patterns, self documenting # Define URL patterns, self documenting
# To see the fancy, colorful evaluation of these use: # To see the fancy, colorful evaluation of these use:
# ./manager show_urls # ./manager show_urls
urlpatterns = [ urlpatterns = [ # pylint: disable=invalid-name
path('new/', CreateView.as_view(), name='new_account'), path('new/', CreateView.as_view(), name='new_account'),
path('login/', LoginView.as_view(template_name='login.html'), path('login/', LoginView.as_view(template_name='login.html'),
name='login'), name='login'),
@@ -38,39 +38,39 @@ urlpatterns = [
path('upload_photo/', UploadPhotoView.as_view(), name='upload_photo'), path('upload_photo/', UploadPhotoView.as_view(), name='upload_photo'),
path('password_set/', PasswordSetView.as_view(), name='password_set'), path('password_set/', PasswordSetView.as_view(), name='password_set'),
url( url(
'remove_unconfirmed_openid/(?P<openid_id>\d+)', r'remove_unconfirmed_openid/(?P<openid_id>\d+)',
RemoveUnconfirmedOpenIDView.as_view(), RemoveUnconfirmedOpenIDView.as_view(),
name='remove_unconfirmed_openid'), name='remove_unconfirmed_openid'),
url( url(
'remove_confirmed_openid/(?P<openid_id>\d+)', r'remove_confirmed_openid/(?P<openid_id>\d+)',
RemoveConfirmedOpenIDView.as_view(), name='remove_confirmed_openid'), RemoveConfirmedOpenIDView.as_view(), name='remove_confirmed_openid'),
url( url(
'openid_redirection/(?P<openid_id>\d+)', r'openid_redirection/(?P<openid_id>\d+)',
RedirectOpenIDView.as_view(), name='openid_redirection'), RedirectOpenIDView.as_view(), name='openid_redirection'),
url( url(
'confirm_openid/(?P<openid_id>\w+)', r'confirm_openid/(?P<openid_id>\w+)',
ConfirmOpenIDView.as_view(), name='confirm_openid'), ConfirmOpenIDView.as_view(), name='confirm_openid'),
url( url(
'confirm_email/(?P<verification_key>\w+)', r'confirm_email/(?P<verification_key>\w+)',
ConfirmEmailView.as_view(), name='confirm_email'), ConfirmEmailView.as_view(), name='confirm_email'),
url( url(
'remove_unconfirmed_email/(?P<email_id>\d+)', r'remove_unconfirmed_email/(?P<email_id>\d+)',
RemoveUnconfirmedEmailView.as_view(), name='remove_unconfirmed_email'), RemoveUnconfirmedEmailView.as_view(), name='remove_unconfirmed_email'),
url( url(
'remove_confirmed_email/(?P<email_id>\d+)', r'remove_confirmed_email/(?P<email_id>\d+)',
RemoveConfirmedEmailView.as_view(), name='remove_confirmed_email'), RemoveConfirmedEmailView.as_view(), name='remove_confirmed_email'),
url( url(
'assign_photo_email/(?P<email_id>\d+)', r'assign_photo_email/(?P<email_id>\d+)',
AssignPhotoEmailView.as_view(), name='assign_photo_email'), AssignPhotoEmailView.as_view(), name='assign_photo_email'),
url( url(
'assign_photo_openid/(?P<openid_id>\d+)', r'assign_photo_openid/(?P<openid_id>\d+)',
AssignPhotoOpenIDView.as_view(), name='assign_photo_openid'), AssignPhotoOpenIDView.as_view(), name='assign_photo_openid'),
url( url(
'import_photo/(?P<email_id>\d+)', r'import_photo/(?P<email_id>\d+)',
ImportPhotoView.as_view(), name='import_photo'), ImportPhotoView.as_view(), name='import_photo'),
url( url(
'delete_photo/(?P<pk>\d+)', r'delete_photo/(?P<pk>\d+)',
DeletePhotoView.as_view(), name='delete_photo'), DeletePhotoView.as_view(), name='delete_photo'),
url('raw_image/(?P<pk>\d+)', RawImageView.as_view(), name='raw_image'), url(r'raw_image/(?P<pk>\d+)', RawImageView.as_view(), name='raw_image'),
url('crop_photo/(?P<pk>\d+)', CropPhotoView.as_view(), name='crop_photo'), url(r'crop_photo/(?P<pk>\d+)', CropPhotoView.as_view(), name='crop_photo'),
] ]

View File

@@ -5,15 +5,14 @@ Django settings for ivatar project.
import os import os
import logging import logging
log_level = logging.DEBUG log_level = logging.DEBUG # pylint: disable=invalid-name
logger = logging.getLogger('ivatar') logger = logging.getLogger('ivatar') # pylint: disable=invalid-name
logger.setLevel(log_level) logger.setLevel(log_level)
PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__)) PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__))
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# TODO: Changeme
# SECURITY WARNING: keep the secret key used in production secret! # 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'
@@ -119,4 +118,4 @@ PROJECT_ROOT = os.path.abspath(
STATIC_URL = '/static/' STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static') STATIC_ROOT = os.path.join(BASE_DIR, 'static')
from config import * # noqa from config import * # pylint: disable=wildcard-import,wrong-import-position,unused-wildcard-import

View File

@@ -1,3 +1,6 @@
'''
Unit tests for WSGI
'''
import unittest import unittest
import os import os
@@ -7,7 +10,13 @@ django.setup()
class TestCase(unittest.TestCase): class TestCase(unittest.TestCase):
'''
Simple testcase to see if WSGI loads correctly
'''
def test_run_wsgi(self): def test_run_wsgi(self):
'''
Run wsgi import
'''
import ivatar.wsgi import ivatar.wsgi
self.assertEqual(ivatar.wsgi.application.__class__, self.assertEqual(ivatar.wsgi.application.__class__,
django.core.handlers.wsgi.WSGIHandler) django.core.handlers.wsgi.WSGIHandler)

View File

@@ -1,5 +1,5 @@
''' '''
ivatar URL Configuration ivatar URL configuration
''' '''
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import path, include
@@ -9,15 +9,15 @@ from django.views.generic import TemplateView
from ivatar import settings from ivatar import settings
from . views import AvatarImageView from . views import AvatarImageView
urlpatterns = [ urlpatterns = [ # pylint: disable=invalid-name
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
url('openid/', include('django_openid_auth.urls')), url('openid/', include('django_openid_auth.urls')),
url('accounts/', include('ivatar.ivataraccount.urls')), url('accounts/', include('ivatar.ivataraccount.urls')),
url( url(
'avatar/(?P<digest>\w{64})', r'avatar/(?P<digest>\w{64})',
AvatarImageView.as_view(), name='avatar_view'), AvatarImageView.as_view(), name='avatar_view'),
url( url(
'avatar/(?P<digest>\w{32})', r'avatar/(?P<digest>\w{32})',
AvatarImageView.as_view(), name='avatar_view'), AvatarImageView.as_view(), name='avatar_view'),
url('', TemplateView.as_view(template_name='home.html')), url('', TemplateView.as_view(template_name='home.html')),
] ]

View File

@@ -1,10 +1,13 @@
'''
Simple module providing reusable random_string function
'''
import random import random
import string import string
def random_string(len=10): def random_string(length=10):
''' '''
Return some random string with default length 10 Return some random string with default length 10
''' '''
return ''.join(random.SystemRandom().choice( return ''.join(random.SystemRandom().choice(
string.ascii_lowercase + string.digits) for _ in range(10)) string.ascii_lowercase + string.digits) for _ in range(length))

View File

@@ -5,6 +5,7 @@ import io
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.http import HttpResponse from django.http import HttpResponse
from . ivataraccount.models import ConfirmedEmail, ConfirmedOpenId from . ivataraccount.models import ConfirmedEmail, ConfirmedOpenId
from django.core.exceptions import ObjectDoesNotExist
class AvatarImageView(TemplateView): class AvatarImageView(TemplateView):
@@ -29,13 +30,14 @@ class AvatarImageView(TemplateView):
try: try:
obj = model.objects.get(digest=kwargs['digest']) obj = model.objects.get(digest=kwargs['digest'])
except model.DoesNotExist: except ObjectDoesNotExist:
# TODO: Use default!? # TODO: Use default!?
raise Exception('Mail/openid ("%s") does not exist"' % raise Exception('Mail/openid ("%s") does not exist"' %
kwargs['digest']) kwargs['digest'])
if not obj.photo: if not obj.photo:
# That is hacky, but achieves what we want :-) # That is hacky, but achieves what we want :-)
attr = getattr(obj, 'email', obj.openid) attr = getattr(obj, 'email', obj.openid)
# TODO: Use default!?
raise Exception('No photo assigned to "%s"' % attr) raise Exception('No photo assigned to "%s"' % attr)
return HttpResponse( return HttpResponse(

View File

@@ -13,4 +13,4 @@ from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ivatar.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ivatar.settings")
application = get_wsgi_application() application = get_wsgi_application() # pylint: disable=invalid-name