diff --git a/config.py b/config.py index 324a4e0..15decaa 100644 --- a/config.py +++ b/config.py @@ -25,6 +25,7 @@ INSTALLED_APPS.extend([ 'anymail', 'ivatar', 'ivatar.ivataraccount', + 'ivatar.tools', ]) from ivatar.settings import MIDDLEWARE # noqa @@ -54,6 +55,9 @@ OPENID_UPDATE_DETAILS_FROM_SREG = True SITE_NAME = 'ivatar' IVATAR_VERSION = '0.1' +SECURE_BASE_URL = 'https://avatars.linux-kernel.at/avatar/' +BASE_URL = 'http://avatars.linux-kernel.at/avatar/' + LOGIN_REDIRECT_URL = reverse_lazy('profile') MAX_LENGTH_EMAIL = 254 # http://stackoverflow.com/questions/386294 SERVER_EMAIL = 'accounts@mg.linux-kernel.at' @@ -65,6 +69,13 @@ MAX_PIXELS = 7000 AVATAR_MAX_SIZE = 512 JPEG_QUALITY = 85 +# I'm not 100% sure if single character domains are possible +# under any tld... so MIN_LENGTH_EMAIL/_URL, might be +1 +MIN_LENGTH_URL = 11 # eg. http://a.io +MAX_LENGTH_URL = 255 # MySQL can't handle more than that (LP: 1018682) +MIN_LENGTH_EMAIL = 6 # eg. x@x.xx +MAX_LENGTH_EMAIL = 254 # http://stackoverflow.com/questions/386294 + BOOTSTRAP4 = { 'include_jquery': False, 'javascript_in_head': False, diff --git a/ivatar/ivataraccount/forms.py b/ivatar/ivataraccount/forms.py index 168d884..60c155c 100644 --- a/ivatar/ivataraccount/forms.py +++ b/ivatar/ivataraccount/forms.py @@ -12,7 +12,8 @@ from django.core.mail import send_mail from ipware import get_client_ip from ivatar import settings -from ivatar.settings import MAX_LENGTH_EMAIL +from ivatar.settings import MIN_LENGTH_EMAIL, MAX_LENGTH_EMAIL +from ivatar.settings import MIN_LENGTH_URL, MAX_LENGTH_URL from ivatar.ivataraccount.models import MAX_LENGTH_URL from . models import UnconfirmedEmail, ConfirmedEmail, Photo from . models import UnconfirmedOpenId, ConfirmedOpenId @@ -26,8 +27,8 @@ class AddEmailForm(forms.Form): ''' email = forms.EmailField( label=_('Email'), + min_length=MIN_LENGTH_EMAIL, max_length=MAX_LENGTH_EMAIL, - min_length=6, # x@x.xx ) def clean_email(self): @@ -136,10 +137,8 @@ class AddOpenIDForm(forms.Form): ''' openid = forms.URLField( label=_('OpenID'), + min_length=MIN_LENGTH_URL, max_length=MAX_LENGTH_URL, - # However, not 100% sure if single character domains are possible - # under any tld... - min_length=11, # eg. http://a.io initial='http://' ) diff --git a/ivatar/ivataraccount/models.py b/ivatar/ivataraccount/models.py index 7ff69eb..c282de7 100644 --- a/ivatar/ivataraccount/models.py +++ b/ivatar/ivataraccount/models.py @@ -365,6 +365,9 @@ class ConfirmedOpenId(BaseAccountModel): lowercase_url = urlunsplit( (url.scheme.lower(), netloc, url.path, url.query, url.fragment) ) + if lowercase_url[-1] != '/': + lowercase_url += '/' + self.openid = lowercase_url self.digest = hashlib.sha256(lowercase_url.encode('utf-8')).hexdigest() return super().save(force_insert, force_update, using, update_fields) diff --git a/ivatar/ivataraccount/test_views.py b/ivatar/ivataraccount/test_views.py index 5f1d133..7c99489 100644 --- a/ivatar/ivataraccount/test_views.py +++ b/ivatar/ivataraccount/test_views.py @@ -38,7 +38,7 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods password = random_string() email = '%s@%s.%s' % (username, random_string(), random_string(2)) # Dunno why random tld doens'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): ''' @@ -822,7 +822,6 @@ class Tester(TestCase): # pylint: disable=too-many-public-methods }, ) self.assertEqual(response.status_code, 302, 'OpenID must redirect') - response = self.client.post( reverse('add_openid'), { 'openid': self.openid, diff --git a/ivatar/tools/forms.py b/ivatar/tools/forms.py new file mode 100644 index 0000000..736bb1c --- /dev/null +++ b/ivatar/tools/forms.py @@ -0,0 +1,72 @@ +''' +Classes for our ivatar.tools.forms +''' +from django import forms +from django.utils.translation import ugettext_lazy as _ +from django.core.exceptions import ValidationError + +from ivatar.settings import AVATAR_MAX_SIZE +from ivatar.settings import MIN_LENGTH_URL, MAX_LENGTH_URL +from ivatar.settings import MIN_LENGTH_EMAIL, MAX_LENGTH_EMAIL + + +class CheckDomainForm(forms.Form): + ''' + Form handling domain check + ''' + can_distribute = forms.TextInput( + attrs={ + 'label': _('Domain'), + 'required': True, + 'error_messages': { + 'required': + _('Cannot check without a domain name.') + } + } + ) + + +class CheckForm(forms.Form): + ''' + Form handling check + ''' + mail = forms.EmailField( + label=_('E-Mail'), + required=False, + min_length=MIN_LENGTH_EMAIL, + max_length=MAX_LENGTH_EMAIL, + error_messages={ + 'required': + _('Cannot check without a domain name.') + }) + + openid = forms.CharField( + label=_('OpenID'), + required=False, + min_length=MIN_LENGTH_URL, + max_length=MAX_LENGTH_URL, + error_messages={ + 'required': + _('Cannot check without an openid name.') + }) + + size = forms.IntegerField( + label=_('Size'), + initial=80, + min_value=5, + max_value=AVATAR_MAX_SIZE, + required=True, + ) + + default_url = forms.URLField( + label=_('Default URL'), + required=False, + ) + + def clean(self): + self.cleaned_data = super().clean() + mail = self.cleaned_data.get('mail') + openid = self.cleaned_data.get('openid') + if not mail and not openid: + raise ValidationError(_('Either OpenID or mail must be specified')) + return self.cleaned_data diff --git a/ivatar/tools/templates/check.html b/ivatar/tools/templates/check.html new file mode 100644 index 0000000..97faab8 --- /dev/null +++ b/ivatar/tools/templates/check.html @@ -0,0 +1,75 @@ +{% extends 'base.html' %} +{% load i18n %} +{% load bootstrap4 %} +{% load static %} + +{% block title %}{% trans 'Check e-mail or openid' %}{% endblock title %} + +{% block content %} + +

{% trans 'Check e-mail or openid' %}

+ +
+
{% csrf_token %} + {% bootstrap_form form %} + {% buttons %} + + + {% endbuttons %} +
+
+ +{% if mailurl or openidurl %} +

+ This is what the avatars will look like depending on the hash and protocol you use:
+ + {% if mail_hash %} + MD5 hash (mail): {{ mail_hash }}
+ SHA256 hash (mail): {{ mail_hash256 }}
+ {% endif %} + + {% if openid_hash %} + SHA256 hash (OpenID): {{ openid_hash }}
+ {% endif %} +

+ + +{% endif %} + +{# Bad hack in order to have the images for sure inside our "outer" div box #} + +{% if mailurl %} +
 
+{% endif %} +{% if openidurl %} +
 
+{% endif %} + + +{% endblock content %} diff --git a/ivatar/tools/urls.py b/ivatar/tools/urls.py new file mode 100644 index 0000000..9605ed4 --- /dev/null +++ b/ivatar/tools/urls.py @@ -0,0 +1,11 @@ +''' +ivatar/tools URL configuration +''' + +from django.conf.urls import url +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'), +] diff --git a/ivatar/tools/views.py b/ivatar/tools/views.py new file mode 100644 index 0000000..6debeb9 --- /dev/null +++ b/ivatar/tools/views.py @@ -0,0 +1,80 @@ +''' +View classes for ivatar/tools/ +''' +from django.views.generic.edit import FormView +from django.urls import reverse_lazy as reverse +from django.shortcuts import render + +from libravatar import libravatar_url, parse_user_identity +from libravatar import SECURE_BASE_URL as LIBRAVATAR_SECURE_BASE_URL +from libravatar import BASE_URL as LIBRAVATAR_BASE_URL +import hashlib + +from .forms import CheckDomainForm, CheckForm +from ivatar.settings import SECURE_BASE_URL, BASE_URL + + +class CheckDomainView(FormView): + ''' + View class for checking a domain + ''' + template_name = 'check_domain.html' + form_class = CheckDomainForm + + +class CheckView(FormView): + ''' + View class for checking an e-mail or openid address + ''' + template_name = 'check.html' + form_class = CheckForm + success_url = reverse('tools_check') + + def form_valid(self, form): + mailurl = None + openidurl = None + mailurl_secure = None + openidurl_secure = None + mail_hash = None + mail_hash256 = None + openid_hash = None + size = 80 + + super().form_valid(form) + + if form.cleaned_data['default_url']: + default_url = form.cleaned_data['default_url'] + else: + default_url = None + + if form.cleaned_data['mail']: + mailurl = libravatar_url(email=form.cleaned_data['mail'], size=form.cleaned_data['size'], default=default_url) + mailurl = mailurl.replace(LIBRAVATAR_BASE_URL, BASE_URL) + mailurl_secure = libravatar_url(email=form.cleaned_data['mail'], size=form.cleaned_data['size'], https=True, default=default_url) + mailurl_secure = mailurl_secure.replace(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')) + mail_hash256 = hash_obj.hexdigest() + size = form.cleaned_data['size'] + if form.cleaned_data['openid']: + if form.cleaned_data['openid'][-1] != '/': + form.cleaned_data['openid'] += '/' + openidurl = libravatar_url(openid=form.cleaned_data['openid'], size=form.cleaned_data['size'], default=default_url) + openidurl = openidurl.replace(LIBRAVATAR_BASE_URL, BASE_URL) + openidurl_secure = libravatar_url(openid=form.cleaned_data['openid'], size=form.cleaned_data['size'], https=True, default=default_url) + openidurl_secure = openidurl_secure.replace(LIBRAVATAR_SECURE_BASE_URL, SECURE_BASE_URL) + openid_hash = parse_user_identity(openid=form.cleaned_data['openid'], email=None)[0] + size = form.cleaned_data['size'] + + return render(self.request, self.template_name, { + 'form': form, + 'mailurl': mailurl, + 'openidurl': openidurl, + 'mailurl_secure': mailurl_secure, + 'openidurl_secure': openidurl_secure, + 'mail_hash': mail_hash, + 'mail_hash256': mail_hash256, + 'openid_hash': openid_hash, + 'size': size, + }) diff --git a/ivatar/urls.py b/ivatar/urls.py index e4be7b4..ed6deb8 100644 --- a/ivatar/urls.py +++ b/ivatar/urls.py @@ -13,6 +13,7 @@ urlpatterns = [ # pylint: disable=invalid-name path('admin/', admin.site.urls), url('openid/', include('django_openid_auth.urls')), url('accounts/', include('ivatar.ivataraccount.urls')), + url('tools/', include('ivatar.tools.urls')), url( r'avatar/(?P\w{64})', AvatarImageView.as_view(), name='avatar_view'), diff --git a/templates/base.html b/templates/base.html index f0d7b88..3894a04 100644 --- a/templates/base.html +++ b/templates/base.html @@ -19,13 +19,16 @@ {% autoescape off %}{% bootstrap_messages %}{% endautoescape %} + {# TODO: Fix URLs!!! #}
{% if user.is_authenticated %} {% trans 'Profile' %} |  + {% trans 'Check' %} |  {% trans 'Contact Us' %} |  {% trans 'Security' %} |  {% trans 'Logout' %} {% else %} + {% trans 'Check' %} |  {% trans 'Contact Us' %} |  {% trans 'Security' %} |  {% trans 'Login' %}