Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
stefankoegl committed Oct 26, 2017
1 parent 518a46d commit b8c81c2
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 234 deletions.
2 changes: 2 additions & 0 deletions mygpo/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,3 +385,5 @@ def get_intOrNone(name, default):

MYGPO_AUTH_URL = os.getenv('MYGPO_AUTH_URL', None)

MYGPO_AUTH_REGISTER_URL = os.getenv('MYGPO_AUTH_REGISTER_URL', None)

13 changes: 13 additions & 0 deletions mygpo/users/checks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.core.checks import register, Warning
from django.db import connection
from django.db.utils import OperationalError
from django.conf import settings


SQL = """
Expand Down Expand Up @@ -38,3 +39,15 @@ def check_case_insensitive_users(app_configs=None, **kwargs):
raise

return errors


@register()
def check_registration_url(app_configs=None, **kwargs):
errors = []

if not settings.MYGPO_AUTH_REGISTER_URL:
txt = 'The setting MYGPO_AUTH_REGISTER_URL is not set.'
wid = 'users.W002'
errors.append(Warning(txt, id=wid))

return errors
16 changes: 0 additions & 16 deletions mygpo/users/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,6 @@
registration.RegistrationView.as_view(),
name='register'),

url(r'^registration_complete/$',
registration.TemplateView.as_view(
template_name='registration/registration_complete.html'),
name='registration-complete'),

url(r'^activate/(?P<activation_key>\w+)$',
registration.ActivationView.as_view()),

url(r'^registration/resend$',
registration.ResendActivationView.as_view(),
name='resend-activation'),

url(r'^registration/resent$',
registration.ResentActivationView.as_view(),
name='resent-activation'),

url(r'^account/$',
settings.account,
name='account'),
Expand Down
217 changes: 5 additions & 212 deletions mygpo/users/views/registration.py
Original file line number Diff line number Diff line change
@@ -1,220 +1,13 @@
import re

from django import forms
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django.db import IntegrityError, transaction
from django.http import HttpResponseRedirect
from django.views.generic.edit import FormView
from django.utils.translation import ugettext as _
from django.template.loader import render_to_string
from django.urls import reverse, reverse_lazy
from django.views.generic import TemplateView
from django.views import View
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.sites.requests import RequestSite
from django.conf import settings

from mygpo.utils import random_token
from mygpo.users.models import UserProxy


USERNAME_MAXLEN = get_user_model()._meta.get_field('username').max_length


class DuplicateUsername(ValidationError):
""" The username is already in use """

def __init__(self, username):
self.username = username
super().__init__('The username {0} is already in use.'
.format(username))


class DuplicateEmail(ValidationError):
""" The email address is already in use """

def __init__(self, email):
self.email = email
super().__init__('The email address {0} is already in use.'
.format(email))


class UsernameValidator(RegexValidator):
""" Validates that a username uses only allowed characters """
regex = r'^\w[\w.+-]*$'
message = 'Invalid Username'
code = 'invalid-username'
flags = re.ASCII


class RegistrationForm(forms.Form):
""" Form that is used to register a new user """
username = forms.CharField(max_length=USERNAME_MAXLEN,
validators=[UsernameValidator()],
)
email = forms.EmailField()
password1 = forms.CharField(widget=forms.PasswordInput())
password2 = forms.CharField(widget=forms.PasswordInput())

def clean(self):
cleaned_data = super(RegistrationForm, self).clean()
password1 = cleaned_data.get('password1')
password2 = cleaned_data.get('password2')

if not password1 or password1 != password2:
raise forms.ValidationError('Passwords do not match')


class RegistrationView(FormView):
class RegistrationView(View):
""" View to register a new user """
template_name = 'registration/registration_form.html'
form_class = RegistrationForm
success_url = reverse_lazy('registration-complete')

def form_valid(self, form):
""" called whene the form was POSTed and its contents were valid """

try:
user = self.create_user(form)

except ValidationError as e:
messages.error(self.request, '; '.join(e.messages))
return HttpResponseRedirect(reverse('register'))

except IntegrityError:
messages.error(self.request,
_('Username or email address already in use'))
return HttpResponseRedirect(reverse('register'))

send_activation_email(user, self.request)
return super(RegistrationView, self).form_valid(form)

@transaction.atomic
def create_user(self, form):
User = get_user_model()
user = User()
username = form.cleaned_data['username']

self._check_username(username)
user.username = username

email_addr = form.cleaned_data['email']
user.email = email_addr

user.set_password(form.cleaned_data['password1'])
user.is_active = False
user.full_clean()

try:
user.save()

except IntegrityError as e:
if 'django_auth_unique_email' in str(e):
# this was not caught by the form validation, but now validates
# the DB's unique constraint
raise DuplicateEmail(email_addr) from e
else:
raise

user.profile.activation_key = random_token()
user.profile.save()

return user

def _check_username(self, username):
""" Check if the username is already in use
Until there is a case-insensitive constraint on usernames, it is
necessary to check for existing usernames manually. This is not a
perfect solution, but the chance that two people sign up with the same
username at the same time is low enough. """
UserModel = get_user_model()
users = UserModel.objects.filter(username__iexact=username)
if users.exists():
raise DuplicateUsername(username)


class ActivationView(TemplateView):
""" Activates an already registered user """

template_name = 'registration/activation_failed.html'

def get(self, request, activation_key):
User = get_user_model()

try:
user = UserProxy.objects.get(
profile__activation_key=activation_key,
is_active=False,
)
except UserProxy.DoesNotExist:
messages.error(request, _('The activation link is either not '
'valid or has already expired.'))
return super(ActivationView, self).get(request, activation_key)

user.activate()
messages.success(request, _('Your user has been activated. '
'You can log in now.'))
return HttpResponseRedirect(reverse('login'))


class ResendActivationForm(forms.Form):
""" Form for resending the activation email """

username = forms.CharField(max_length=USERNAME_MAXLEN, required=False)
email = forms.EmailField(required=False)

def clean(self):
cleaned_data = super(ResendActivationForm, self).clean()
username = cleaned_data.get('username')
email = cleaned_data.get('email')

if not username and not email:
raise forms.ValidationError(_('Either username or email address '
'are required.'))


class ResendActivationView(FormView):
""" View to resend the activation email """
template_name = 'registration/resend_activation.html'
form_class = ResendActivationForm
success_url = reverse_lazy('resent-activation')

def form_valid(self, form):
""" called whene the form was POSTed and its contents were valid """

try:
user = UserProxy.objects.all().by_username_or_email(
form.cleaned_data['username'],
form.cleaned_data['email'],
)

except UserProxy.DoesNotExist:
messages.error(self.request, _('User does not exist.'))
return HttpResponseRedirect(reverse('resend-activation'))

if user.profile.activation_key is None:
messages.success(self.request, _('Your account already has been '
'activated. Go ahead and log in.'))

send_activation_email(user, self.request)
return super(ResendActivationView, self).form_valid(form)


class ResentActivationView(TemplateView):
template_name = 'registration/resent_activation.html'


def send_activation_email(user, request):
""" Sends the activation email for the given user """

subj = render_to_string('registration/activation_email_subject.txt')
# remove trailing newline added by render_to_string
subj = subj.strip()

msg = render_to_string('registration/activation_email.txt', {
'site': RequestSite(request),
'activation_key': user.profile.activation_key,
})
user.email_user(subj, msg)
def get(self, request):
url = settings.MYGPO_AUTH_REGISTER_URL
return HttpResponseRedirect(url)
12 changes: 6 additions & 6 deletions mygpo/users/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
from mygpo.constants import DEFAULT_LOGIN_REDIRECT
from mygpo.web.auth import get_google_oauth_flow
from mygpo.users.models import UserProxy
from mygpo.users.views.registration import send_activation_email
from mygpo.utils import random_token

import logging
Expand Down Expand Up @@ -83,11 +82,11 @@ def post(self, request):
messages.error(request, _('Wrong username or password.'))
return HttpResponseRedirect(login_page)


if not user.is_active:
send_activation_email(user, request)
# send_activation_email(user, request)
messages.error(request, _('Please activate your account first. '
'We have just re-sent your activation email'))
'We have just re-sent your activation '
'email'))
return HttpResponseRedirect(login_page)

# set up the user's session
Expand Down Expand Up @@ -130,9 +129,10 @@ def restore_password(request):
return render(request, 'password_reset_failed.html')

if not user.is_active:
send_activation_email(user, request)
# send_activation_email(user, request)
messages.error(request, _('Please activate your account first. '
'We have just re-sent your activation email'))
'We have just re-sent your activation '
'email'))
return HttpResponseRedirect(reverse('login'))

site = RequestSite(request)
Expand Down

0 comments on commit b8c81c2

Please sign in to comment.