core: add placeholders for forms, add sign-up view

This commit is contained in:
Jens Langhammer 2018-12-10 13:51:38 +01:00
parent cc12f1d8b3
commit 2a500b3e4a
2 changed files with 157 additions and 4 deletions

View File

@ -1,16 +1,22 @@
"""passbook core authentication forms""" """passbook core authentication forms"""
from logging import getLogger
from django import forms from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import validate_email from django.core.validators import validate_email
from django.utils.translation import gettext_lazy as _
from passbook.core.models import User
from passbook.lib.config import CONFIG from passbook.lib.config import CONFIG
LOGGER = getLogger(__name__)
class LoginForm(forms.Form): class LoginForm(forms.Form):
"""Allow users to login""" """Allow users to login"""
uid_field = forms.CharField() title = _('Log in to your account')
password = forms.CharField(widget=forms.PasswordInput()) uid_field = forms.CharField(widget=forms.TextInput(attrs={'placeholder': _('UID')}))
password = forms.CharField(widget=forms.PasswordInput(attrs={'placeholder': _('Password')}))
remember_me = forms.BooleanField(required=False) remember_me = forms.BooleanField(required=False)
def clean_uid_field(self): def clean_uid_field(self):
@ -18,3 +24,55 @@ class LoginForm(forms.Form):
if CONFIG.y('passbook.uid_fields') == ['email']: if CONFIG.y('passbook.uid_fields') == ['email']:
validate_email(self.cleaned_data.get('uid_field')) validate_email(self.cleaned_data.get('uid_field'))
return self.cleaned_data.get('uid_field') return self.cleaned_data.get('uid_field')
class SignUpForm(forms.Form):
"""SignUp Form"""
title = _('Sign Up')
first_name = forms.CharField(label=_('First Name'),
widget=forms.TextInput(attrs={'placeholder': _('First Name')}))
last_name = forms.CharField(label=_('Last Name'),
widget=forms.TextInput(attrs={'placeholder': _('Last Name')}))
username = forms.CharField(label=_('Username'),
widget=forms.TextInput(attrs={'placeholder': _('Username')}))
email = forms.EmailField(label=_('E-Mail'),
widget=forms.TextInput(attrs={'placeholder': _('E-Mail')}))
password = forms.CharField(label=_('Password'),
widget=forms.PasswordInput(attrs={'placeholder': _('Password')}))
password_repeat = forms.CharField(label=_('Repeat Password'),
widget=forms.PasswordInput(attrs={
'placeholder': _('Repeat Password')
}))
# captcha = ReCaptchaField(
# required=(not settings.DEBUG and not settings.TEST),
# private_key=Setting.get('recaptcha:private'),
# public_key=Setting.get('recaptcha:public'))
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# TODO: Dynamically add captcha here
# if not Setting.get_bool('recaptcha:enabled'):
# self.fields.pop('captcha')
def clean_username(self):
"""Check if username is used already"""
username = self.cleaned_data.get('username')
if User.objects.filter(username=username).exists():
LOGGER.warning("Username %s already exists", username)
raise ValidationError(_("Username already exists"))
return username
def clean_email(self):
"""Check if email is already used in django or other auth sources"""
email = self.cleaned_data.get('email')
# Check if user exists already, error early
if User.objects.filter(email=email).exists():
LOGGER.debug("email %s exists in django", email)
raise ValidationError(_("Email already exists"))
return email
def clean_password_repeat(self):
"""Check if Password adheres to filter and if passwords matche"""
# TODO: Password policy? Via Plugin? via Policy?
# return check_password(self)
return self.cleaned_data.get('password_repeat')

View File

@ -11,8 +11,8 @@ from django.utils.translation import ugettext as _
from django.views import View from django.views import View
from django.views.generic import FormView from django.views.generic import FormView
from passbook.core.forms.authentication import LoginForm from passbook.core.forms.authentication import LoginForm, SignUpForm
from passbook.core.models import User from passbook.core.models import Invite, User
from passbook.lib.config import CONFIG from passbook.lib.config import CONFIG
LOGGER = getLogger(__name__) LOGGER = getLogger(__name__)
@ -41,6 +41,10 @@ class LoginView(UserPassesTestMixin, FormView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['config'] = CONFIG.get('passbook') kwargs['config'] = CONFIG.get('passbook')
kwargs['is_login'] = True kwargs['is_login'] = True
kwargs['title'] = _('Log in to your account')
kwargs['primary_action'] = _('Log in')
kwargs['show_sign_up_notice'] = CONFIG.y('passbook.sign_up.enabled')
kwargs['show_password_forget_notice'] = CONFIG.y('passbook.password_reset.enabled')
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_user(self, uid_value) -> User: def get_user(self, uid_value) -> User:
@ -105,3 +109,94 @@ class LogoutView(LoginRequiredMixin, View):
logout(request) logout(request)
messages.success(request, _("You've successfully been logged out.")) messages.success(request, _("You've successfully been logged out."))
return redirect(reverse('passbook_core:auth-login')) return redirect(reverse('passbook_core:auth-login'))
class SignUpView(UserPassesTestMixin, FormView):
"""Sign up new user, optionally consume one-use invite link."""
template_name = 'login/form.html'
form_class = SignUpForm
success_url = '.'
_invite = None
# Allow only not authenticated users to login
def test_func(self):
return self.request.user.is_authenticated is False
def handle_no_permission(self):
return redirect(reverse('passbook_core:overview'))
def dispatch(self, request, *args, **kwargs):
"""Check if sign-up is enabled or invite link given"""
allowed = False
if 'invite' in request.GET:
invites = Invite.objects.filter(uuid=request.GET.get('invite'))
allowed = invites.exists()
if allowed:
self._invite = invites.first()
if CONFIG.y('passbook.sign_up.enabled'):
allowed = True
if not allowed:
messages.error(request, _('Sign-ups are currently disabled.'))
return redirect(reverse('passbook_core:auth-login'))
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
kwargs['config'] = CONFIG.get('passbook')
kwargs['is_login'] = True
kwargs['title'] = _('Sign Up')
kwargs['primary_action'] = _('Sign up')
return super().get_context_data(**kwargs)
def form_valid(self, form: SignUpForm) -> HttpResponse:
"""Create user"""
SignUpView.create_user(form.cleaned_data, self.request)
if self._invite:
self._invite.delete()
messages.success(self.request, _("Successfully signed up!"))
LOGGER.debug("Successfully signed up %s",
form.cleaned_data.get('email'))
return redirect(reverse('passbook_core:auth-login'))
@staticmethod
def create_user(data: Dict, request: HttpRequest = None) -> User:
"""Create user from data
Args:
data: Dictionary as returned by SignupForm's cleaned_data
request: Optional current request.
Returns:
The user created
Raises:
SignalException: if any signals raise an exception. This also deletes the created user.
"""
# Create user
new_user = User.objects.create_user(
username=data.get('username'),
email=data.get('email'),
first_name=data.get('first_name'),
last_name=data.get('last_name'),
)
new_user.is_active = True
new_user.set_password(data.get('password'))
new_user.save()
# Send signal for other auth sources
# try:
# TODO: Create signal for signup
# on_user_sign_up.send(
# sender=None,
# user=new_user,
# request=request,
# password=data.get('password'),
# needs_confirmation=needs_confirmation)
# TODO: Implement Verification, via email or others
# if needs_confirmation:
# Create Account Confirmation UUID
# AccountConfirmation.objects.create(user=new_user)
# except SignalException as exception:
# LOGGER.warning("Failed to sign up user %s", exception, exc_info=exception)
# new_user.delete()
# raise
return new_user