mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
178 lines
6.0 KiB
Python
178 lines
6.0 KiB
Python
"""Core forms."""
|
|
|
|
import oath
|
|
|
|
from django import forms
|
|
from django.contrib.auth import (
|
|
forms as auth_forms, get_user_model, password_validation
|
|
)
|
|
from django.db.models import Q
|
|
from django.utils.translation import gettext as _, gettext_lazy
|
|
|
|
import django_otp
|
|
|
|
from modoboa.core.models import User
|
|
from modoboa.lib.form_utils import UserKwargModelFormMixin
|
|
from modoboa.parameters import tools as param_tools
|
|
|
|
|
|
class LoginForm(forms.Form):
|
|
"""User login form."""
|
|
|
|
username = forms.CharField(
|
|
label=gettext_lazy("Username"),
|
|
widget=forms.TextInput(attrs={"class": "form-control"})
|
|
)
|
|
password = forms.CharField(
|
|
label=gettext_lazy("Password"),
|
|
widget=forms.PasswordInput(attrs={"class": "form-control"})
|
|
)
|
|
rememberme = forms.BooleanField(
|
|
initial=False,
|
|
required=False
|
|
)
|
|
|
|
|
|
class ProfileForm(forms.ModelForm):
|
|
"""Form to update User profile."""
|
|
|
|
oldpassword = forms.CharField(
|
|
label=gettext_lazy("Old password"), required=False,
|
|
widget=forms.PasswordInput(attrs={"class": "form-control"})
|
|
)
|
|
newpassword = forms.CharField(
|
|
label=gettext_lazy("New password"), required=False,
|
|
widget=forms.PasswordInput(attrs={"class": "form-control"})
|
|
)
|
|
confirmation = forms.CharField(
|
|
label=gettext_lazy("Confirmation"), required=False,
|
|
widget=forms.PasswordInput(attrs={"class": "form-control"})
|
|
)
|
|
|
|
class Meta:
|
|
model = User
|
|
fields = ("first_name", "last_name", "language",
|
|
"phone_number", "secondary_email")
|
|
widgets = {
|
|
"first_name": forms.TextInput(attrs={"class": "form-control"}),
|
|
"last_name": forms.TextInput(attrs={"class": "form-control"})
|
|
}
|
|
|
|
def __init__(self, update_password, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
if not update_password:
|
|
del self.fields["oldpassword"]
|
|
del self.fields["newpassword"]
|
|
del self.fields["confirmation"]
|
|
|
|
def clean_oldpassword(self):
|
|
if self.cleaned_data["oldpassword"] == "":
|
|
return self.cleaned_data["oldpassword"]
|
|
|
|
if param_tools.get_global_parameter("authentication_type") != "local":
|
|
return self.cleaned_data["oldpassword"]
|
|
|
|
if not self.instance.check_password(self.cleaned_data["oldpassword"]):
|
|
raise forms.ValidationError(_("Old password mismatchs"))
|
|
return self.cleaned_data["oldpassword"]
|
|
|
|
def clean(self):
|
|
super().clean()
|
|
if self.errors:
|
|
return self.cleaned_data
|
|
oldpassword = self.cleaned_data.get("oldpassword")
|
|
newpassword = self.cleaned_data.get("newpassword")
|
|
confirmation = self.cleaned_data.get("confirmation")
|
|
if newpassword and confirmation:
|
|
if oldpassword:
|
|
if newpassword != confirmation:
|
|
self.add_error("confirmation", _("Passwords mismatch"))
|
|
else:
|
|
password_validation.validate_password(
|
|
confirmation, self.instance)
|
|
else:
|
|
self.add_error("oldpassword", _("This field is required."))
|
|
elif newpassword or confirmation:
|
|
if not confirmation:
|
|
self.add_error("confirmation", _("This field is required."))
|
|
else:
|
|
self.add_error("newpassword", _("This field is required."))
|
|
return self.cleaned_data
|
|
|
|
def save(self, commit=True):
|
|
user = super().save(commit=False)
|
|
if commit:
|
|
if self.cleaned_data.get("confirmation", "") != "":
|
|
user.set_password(
|
|
self.cleaned_data["confirmation"],
|
|
self.cleaned_data["oldpassword"]
|
|
)
|
|
user.save()
|
|
return user
|
|
|
|
|
|
class APIAccessForm(forms.Form):
|
|
"""Form to control API access."""
|
|
|
|
enable_api_access = forms.BooleanField(
|
|
label=gettext_lazy("Enable API access"), required=False)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
"""Initialize form."""
|
|
user = kwargs.pop("user")
|
|
super(APIAccessForm, self).__init__(*args, **kwargs)
|
|
self.fields["enable_api_access"].initial = hasattr(user, "auth_token")
|
|
|
|
|
|
class PasswordResetForm(auth_forms.PasswordResetForm):
|
|
"""Custom password reset form."""
|
|
|
|
def get_users(self, email):
|
|
"""Return matching user(s) who should receive a reset."""
|
|
return (
|
|
get_user_model()._default_manager.filter(
|
|
email__iexact=email, is_active=True)
|
|
.exclude(Q(secondary_email__isnull=True) | Q(secondary_email=""))
|
|
)
|
|
|
|
def send_mail(self, subject_template_name, email_template_name,
|
|
context, from_email, to_email,
|
|
html_email_template_name=None):
|
|
"""Send message to secondary email instead."""
|
|
to_email = context["user"].secondary_email
|
|
super(PasswordResetForm, self).send_mail(
|
|
subject_template_name, email_template_name,
|
|
context, from_email, to_email, html_email_template_name)
|
|
|
|
|
|
class VerifySMSCodeForm(forms.Form):
|
|
"""A form to verify a code received by SMS."""
|
|
|
|
code = forms.CharField(
|
|
label=gettext_lazy("Verification code"),
|
|
widget=forms.widgets.TextInput(attrs={"class": "form-control"})
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.totp_secret = kwargs.pop("totp_secret")
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def clean_code(self):
|
|
code = self.cleaned_data["code"]
|
|
if not oath.accept_totp(self.totp_secret, code)[0]:
|
|
raise forms.ValidationError(_("Invalid code"))
|
|
return code
|
|
|
|
|
|
class Verify2FACodeForm(UserKwargModelFormMixin, forms.Form):
|
|
"""A form to verify 2FA codes validity."""
|
|
|
|
tfa_code = forms.CharField()
|
|
|
|
def clean_tfa_code(self):
|
|
code = self.cleaned_data["tfa_code"]
|
|
device = django_otp.match_token(self.user, code)
|
|
if device is None:
|
|
raise forms.ValidationError(_("This code is invalid"))
|
|
return device
|