from django import forms from django.contrib.auth import (authenticate, get_user_model) from django.contrib.auth.forms import UsernameField from django.utils.text import capfirst from django.utils.translation import gettext_lazy as _ UserModel = get_user_model() class OTPAuthenticationForm(forms.Form): """ Base class for authenticating users. Extend this to get a form that accepts username/password logins. """ phone_number = UsernameField(widget=forms.TextInput(attrs={'autofocus': True})) otp = forms.CharField( label=_("otp"), strip=False, widget=forms.PasswordInput(attrs={'autocomplete': 'current-password'}), ) error_messages = { 'invalid_login': _( "Please enter a correct %(phone_number)s and otp. Note that both " "fields may be case-sensitive." ), 'inactive': _("This account is inactive."), } def __init__(self, request=None, *args, **kwargs): """ The 'request' parameter is set for custom auth use by subclasses. The form data comes in via the standard 'data' kwarg. """ self.request = request self.user_cache = None super().__init__(*args, **kwargs) # Set the max length and label for the "username" field. self.phone_number_field = UserModel._meta.get_field('phone_number') phone_number_max_length = self.phone_number_field.max_length or 24 self.fields['phone_number'].max_length = phone_number_max_length self.fields['phone_number'].widget.attrs['maxlength'] = phone_number_max_length if self.fields['phone_number'].label is None: self.fields['phone_number'].label = capfirst(self.phone_number_field.verbose_name) def clean(self): phone_number = self.cleaned_data.get('phone_number') otp = self.cleaned_data.get('otp') if phone_number is not None and otp: self.user_cache = authenticate(self.request, phone_number=phone_number, otp=otp) if self.user_cache is None: raise self.get_invalid_login_error() else: self.confirm_login_allowed(self.user_cache) return self.cleaned_data def confirm_login_allowed(self, user): """ Controls whether the given User may log in. This is a policy setting, independent of end-user authentication. This default behavior is to allow login by active users, and reject login by inactive users. If the given user cannot log in, this method should raise a ``forms.ValidationError``. If the given user may log in, this method should return None. """ if not user.is_active: raise forms.ValidationError( self.error_messages['inactive'], code='inactive', ) def get_user(self): return self.user_cache def get_invalid_login_error(self): return forms.ValidationError( self.error_messages['invalid_login'], code='invalid_login', params={'phone_number': self.phone_number_field.verbose_name}, ) class ProfileUpdateForm(forms.ModelForm): class Meta: model = UserModel fields = ['name', 'first_name', 'last_name', 'email', 'iban', 'avatar']