core: add placeholders for forms, add sign-up view
This commit is contained in:
		| @ -1,16 +1,22 @@ | ||||
| """passbook core authentication forms""" | ||||
| from logging import getLogger | ||||
|  | ||||
| from django import forms | ||||
| from django.core.exceptions import ValidationError | ||||
| 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 | ||||
|  | ||||
| LOGGER = getLogger(__name__) | ||||
|  | ||||
| class LoginForm(forms.Form): | ||||
|     """Allow users to login""" | ||||
|  | ||||
|     uid_field = forms.CharField() | ||||
|     password = forms.CharField(widget=forms.PasswordInput()) | ||||
|     title = _('Log in to your account') | ||||
|     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) | ||||
|  | ||||
|     def clean_uid_field(self): | ||||
| @ -18,3 +24,55 @@ class LoginForm(forms.Form): | ||||
|         if CONFIG.y('passbook.uid_fields') == ['email']: | ||||
|             validate_email(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') | ||||
|  | ||||
| @ -11,8 +11,8 @@ from django.utils.translation import ugettext as _ | ||||
| from django.views import View | ||||
| from django.views.generic import FormView | ||||
|  | ||||
| from passbook.core.forms.authentication import LoginForm | ||||
| from passbook.core.models import User | ||||
| from passbook.core.forms.authentication import LoginForm, SignUpForm | ||||
| from passbook.core.models import Invite, User | ||||
| from passbook.lib.config import CONFIG | ||||
|  | ||||
| LOGGER = getLogger(__name__) | ||||
| @ -41,6 +41,10 @@ class LoginView(UserPassesTestMixin, FormView): | ||||
|     def get_context_data(self, **kwargs): | ||||
|         kwargs['config'] = CONFIG.get('passbook') | ||||
|         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) | ||||
|  | ||||
|     def get_user(self, uid_value) -> User: | ||||
| @ -105,3 +109,94 @@ class LogoutView(LoginRequiredMixin, View): | ||||
|         logout(request) | ||||
|         messages.success(request, _("You've successfully been logged out.")) | ||||
|         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 | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer