374 lines
13 KiB
Python
374 lines
13 KiB
Python
|
from django import forms
|
||
|
|
||
|
from .models import CustomUser, BillingAddress, UserBillingAddress
|
||
|
from django.utils.translation import gettext_lazy as _
|
||
|
import datetime
|
||
|
import logging
|
||
|
import subprocess
|
||
|
import tempfile
|
||
|
import xml
|
||
|
|
||
|
from django import forms
|
||
|
from django.conf import settings
|
||
|
from django.contrib.auth import authenticate
|
||
|
from django.utils.translation import gettext_lazy as _
|
||
|
|
||
|
from .models import CustomUser
|
||
|
from .models import UserHostingKey, GenericProduct
|
||
|
|
||
|
logger = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
class ResendActivationEmailForm(forms.Form):
|
||
|
email = forms.CharField(widget=forms.EmailInput())
|
||
|
|
||
|
class Meta:
|
||
|
fields = ['email']
|
||
|
|
||
|
def clean_email(self):
|
||
|
email = self.cleaned_data.get('email')
|
||
|
try:
|
||
|
c = CustomUser.objects.get(email=email)
|
||
|
if c.validated == 1:
|
||
|
raise forms.ValidationError(
|
||
|
_("The account is already active."))
|
||
|
return email
|
||
|
except CustomUser.DoesNotExist:
|
||
|
raise forms.ValidationError(_("User does not exist"))
|
||
|
|
||
|
|
||
|
class PasswordResetRequestForm(forms.Form):
|
||
|
email = forms.CharField(widget=forms.EmailInput())
|
||
|
|
||
|
class Meta:
|
||
|
fields = ['email']
|
||
|
|
||
|
def clean_email(self):
|
||
|
email = self.cleaned_data.get('email')
|
||
|
try:
|
||
|
CustomUser.objects.get(email=email)
|
||
|
return email
|
||
|
except CustomUser.DoesNotExist:
|
||
|
raise forms.ValidationError(_("User does not exist"))
|
||
|
|
||
|
|
||
|
class SetPasswordForm(forms.Form):
|
||
|
"""
|
||
|
A form that lets a user change set their password without entering the old
|
||
|
password
|
||
|
"""
|
||
|
error_messages = {
|
||
|
'password_mismatch': _("The two password fields didn't match."),
|
||
|
}
|
||
|
new_password1 = forms.CharField(label=_("New password"),
|
||
|
widget=forms.PasswordInput)
|
||
|
new_password2 = forms.CharField(label=_("New password confirmation"),
|
||
|
widget=forms.PasswordInput)
|
||
|
|
||
|
def clean_new_password2(self):
|
||
|
password1 = self.cleaned_data.get('new_password1')
|
||
|
password2 = self.cleaned_data.get('new_password2')
|
||
|
if password1 and password2:
|
||
|
if password1 != password2:
|
||
|
raise forms.ValidationError(
|
||
|
self.error_messages['password_mismatch'],
|
||
|
code='password_mismatch', )
|
||
|
return password2
|
||
|
|
||
|
|
||
|
class EditCreditCardForm(forms.Form):
|
||
|
token = forms.CharField(widget=forms.HiddenInput())
|
||
|
|
||
|
|
||
|
class BillingAddressForm(forms.ModelForm):
|
||
|
token = forms.CharField(widget=forms.HiddenInput(), required=False)
|
||
|
card = forms.CharField(widget=forms.HiddenInput(), required=False)
|
||
|
|
||
|
class Meta:
|
||
|
model = BillingAddress
|
||
|
fields = ['cardholder_name', 'street_address',
|
||
|
'city', 'postal_code', 'country', 'vat_number']
|
||
|
labels = {
|
||
|
'cardholder_name': _('Cardholder Name'),
|
||
|
'street_address': _('Street Address'),
|
||
|
'city': _('City'),
|
||
|
'postal_code': _('Postal Code'),
|
||
|
'Country': _('Country'),
|
||
|
'VAT Number': _('VAT Number')
|
||
|
}
|
||
|
|
||
|
|
||
|
class BillingAddressFormSignup(BillingAddressForm):
|
||
|
name = forms.CharField(label=_('Name'))
|
||
|
email = forms.EmailField(label=_('Email Address'))
|
||
|
field_order = ['name', 'email']
|
||
|
|
||
|
class Meta:
|
||
|
model = BillingAddress
|
||
|
fields = ['name', 'email', 'cardholder_name', 'street_address',
|
||
|
'city', 'postal_code', 'country', 'vat_number']
|
||
|
labels = {
|
||
|
'name': 'Name',
|
||
|
'email': _('Email'),
|
||
|
'cardholder_name': _('Cardholder Name'),
|
||
|
'street_address': _('Street Address'),
|
||
|
'city': _('City'),
|
||
|
'postal_code': _('Postal Code'),
|
||
|
'Country': _('Country'),
|
||
|
'vat_number': _('VAT Number')
|
||
|
}
|
||
|
|
||
|
def clean_email(self):
|
||
|
email = self.cleaned_data.get('email')
|
||
|
try:
|
||
|
CustomUser.objects.get(email=email)
|
||
|
raise forms.ValidationError(
|
||
|
_("The email %(email)s is already registered with us. "
|
||
|
"Please reset your password and access your account.") %
|
||
|
{'email': email}
|
||
|
)
|
||
|
except CustomUser.DoesNotExist:
|
||
|
return email
|
||
|
|
||
|
|
||
|
class UserBillingAddressForm(forms.ModelForm):
|
||
|
user = forms.ModelChoiceField(queryset=CustomUser.objects.all(),
|
||
|
widget=forms.HiddenInput())
|
||
|
|
||
|
class Meta:
|
||
|
model = UserBillingAddress
|
||
|
fields = ['cardholder_name', 'street_address',
|
||
|
'city', 'postal_code', 'country', 'user', 'vat_number']
|
||
|
labels = {
|
||
|
'cardholder_name': _('Cardholder Name'),
|
||
|
'street_address': _('Street Building'),
|
||
|
'city': _('City'),
|
||
|
'postal_code': _('Postal Code'),
|
||
|
'Country': _('Country'),
|
||
|
'vat_number': _('VAT Number'),
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
def generate_ssh_key_name():
|
||
|
return '{prefix}{date_time_str}'.format(
|
||
|
prefix=settings.DCL_SSH_KEY_NAME_PREFIX,
|
||
|
date_time_str=datetime.datetime.now().strftime('%m%d%y%H%M%S')
|
||
|
)
|
||
|
|
||
|
|
||
|
class HostingUserLoginForm(forms.Form):
|
||
|
email = forms.CharField(widget=forms.EmailInput())
|
||
|
password = forms.CharField(widget=forms.PasswordInput())
|
||
|
|
||
|
class Meta:
|
||
|
fields = ['email', 'password']
|
||
|
|
||
|
def clean(self):
|
||
|
email = self.cleaned_data.get('email')
|
||
|
password = self.cleaned_data.get('password')
|
||
|
if self.errors:
|
||
|
return self.cleaned_data
|
||
|
is_auth = authenticate(email=email, password=password)
|
||
|
if not is_auth:
|
||
|
raise forms.ValidationError(
|
||
|
_("Your username and/or password were incorrect."))
|
||
|
elif is_auth.validated == 0:
|
||
|
raise forms.ValidationError(
|
||
|
_("Your account is not activated yet."))
|
||
|
return self.cleaned_data
|
||
|
|
||
|
def clean_email(self):
|
||
|
email = self.cleaned_data.get('email')
|
||
|
try:
|
||
|
CustomUser.objects.get(email=email)
|
||
|
return email
|
||
|
except CustomUser.DoesNotExist:
|
||
|
raise forms.ValidationError(_("User does not exist"))
|
||
|
|
||
|
|
||
|
class ProductModelChoiceField(forms.ModelChoiceField):
|
||
|
def label_from_instance(self, obj):
|
||
|
return obj.product_name
|
||
|
|
||
|
|
||
|
class GenericPaymentForm(forms.Form):
|
||
|
product_name = ProductModelChoiceField(
|
||
|
queryset=GenericProduct.objects.all().order_by('product_name'),
|
||
|
empty_label=_("Choose a product"),
|
||
|
)
|
||
|
amount = forms.FloatField(
|
||
|
widget=forms.TextInput(
|
||
|
attrs={'placeholder': _('Amount in CHF'),
|
||
|
'readonly': 'readonly', }
|
||
|
),
|
||
|
max_value=999999,
|
||
|
min_value=1,
|
||
|
label=_('Amount in CHF')
|
||
|
)
|
||
|
recurring = forms.BooleanField(required=False,
|
||
|
label=_("Recurring monthly"), )
|
||
|
description = forms.CharField(
|
||
|
widget=forms.Textarea(attrs={'style': "height: 60px;"}),
|
||
|
required=False
|
||
|
)
|
||
|
|
||
|
class Meta:
|
||
|
model = GenericProduct
|
||
|
fields = ['product_name', 'amount', 'recurring', 'description']
|
||
|
|
||
|
def clean_amount(self):
|
||
|
amount = self.cleaned_data.get('amount')
|
||
|
if (float(self.cleaned_data.get('product_name').get_actual_price()) !=
|
||
|
amount):
|
||
|
raise forms.ValidationError(_("Amount field does not match"))
|
||
|
return amount
|
||
|
|
||
|
def clean_recurring(self):
|
||
|
recurring = self.cleaned_data.get('recurring')
|
||
|
if (self.cleaned_data.get('product_name').product_is_subscription !=
|
||
|
(True if recurring else False)):
|
||
|
raise forms.ValidationError(_("Recurring field does not match"))
|
||
|
return recurring
|
||
|
|
||
|
|
||
|
class ProductPaymentForm(GenericPaymentForm):
|
||
|
def __init__(self, *args, **kwargs):
|
||
|
product_id = kwargs.pop('product_id', None)
|
||
|
if product_id is not None:
|
||
|
self.product = GenericProduct.objects.get(id=product_id)
|
||
|
super(ProductPaymentForm, self).__init__(*args, **kwargs)
|
||
|
self.fields['product_name'] = forms.CharField(
|
||
|
widget=forms.TextInput(
|
||
|
attrs={'placeholder': _('Product name'),
|
||
|
'readonly': 'readonly'}
|
||
|
)
|
||
|
)
|
||
|
if self.product.product_is_subscription:
|
||
|
payment_type = "month"
|
||
|
if self.product.product_subscription_interval == "month":
|
||
|
payment_type = _('Monthly subscription')
|
||
|
elif self.product.product_subscription_interval == "year":
|
||
|
payment_type = _('Yearly subscription')
|
||
|
self.fields['amount'].label = "{amt} ({payment_type})".format(
|
||
|
amt=_('Amount in CHF'),
|
||
|
payment_type=payment_type
|
||
|
)
|
||
|
else:
|
||
|
self.fields['amount'].label = "{amt} ({payment_type})".format(
|
||
|
amt=_('Amount in CHF'),
|
||
|
payment_type=_('One time payment')
|
||
|
)
|
||
|
self.fields['recurring'].widget = forms.HiddenInput()
|
||
|
self.fields['product_name'].widget.attrs['class'] = 'input-no-border'
|
||
|
self.fields['amount'].widget.attrs['class'] = 'input-no-border'
|
||
|
self.fields['description'].widget.attrs['class'] = 'input-no-border'
|
||
|
|
||
|
def clean_amount(self):
|
||
|
amount = self.cleaned_data.get('amount')
|
||
|
if (self.product is None or
|
||
|
float(self.product.get_actual_price()) != amount):
|
||
|
raise forms.ValidationError(_("Amount field does not match"))
|
||
|
return amount
|
||
|
|
||
|
def clean_recurring(self):
|
||
|
recurring = self.cleaned_data.get('recurring')
|
||
|
if (self.product.product_is_subscription !=
|
||
|
(True if recurring else False)):
|
||
|
raise forms.ValidationError(_("Recurring field does not match"))
|
||
|
return recurring
|
||
|
|
||
|
|
||
|
class HostingUserSignupForm(forms.ModelForm):
|
||
|
confirm_password = forms.CharField(label=_("Confirm Password"),
|
||
|
widget=forms.PasswordInput())
|
||
|
password = forms.CharField(label=_("Password"),
|
||
|
widget=forms.PasswordInput())
|
||
|
|
||
|
class Meta:
|
||
|
model = CustomUser
|
||
|
fields = ['name', 'email', 'password']
|
||
|
widgets = {
|
||
|
'name': forms.TextInput(
|
||
|
attrs={'placeholder': _('Enter your name or company name')}),
|
||
|
}
|
||
|
|
||
|
def clean_confirm_password(self):
|
||
|
password = self.cleaned_data.get('password')
|
||
|
confirm_password = self.cleaned_data.get('confirm_password')
|
||
|
if not confirm_password == password:
|
||
|
raise forms.ValidationError("Passwords don't match")
|
||
|
return confirm_password
|
||
|
|
||
|
|
||
|
class UserHostingKeyForm(forms.ModelForm):
|
||
|
private_key = forms.CharField(widget=forms.HiddenInput(), required=False)
|
||
|
public_key = forms.CharField(widget=forms.Textarea(
|
||
|
attrs={'class': 'form_public_key',
|
||
|
'placeholder': _('Paste here your public key')}),
|
||
|
required=False,
|
||
|
)
|
||
|
user = forms.models.ModelChoiceField(queryset=CustomUser.objects.all(),
|
||
|
required=False,
|
||
|
widget=forms.HiddenInput())
|
||
|
name = forms.CharField(required=False, widget=forms.TextInput(
|
||
|
attrs={'class': 'form_key_name',
|
||
|
'placeholder': _('Give a name to your key')}))
|
||
|
|
||
|
def __init__(self, *args, **kwargs):
|
||
|
self.request = kwargs.pop("request")
|
||
|
super(UserHostingKeyForm, self).__init__(*args, **kwargs)
|
||
|
self.fields['name'].label = _('Key name')
|
||
|
|
||
|
def clean_public_key(self):
|
||
|
"""
|
||
|
Validates a public ssh key using `ssh-keygen -lf key.pub`
|
||
|
Also checks if a given key already exists in the database and
|
||
|
alerts the user of it.
|
||
|
:return:
|
||
|
"""
|
||
|
if ('generate' in self.request.POST
|
||
|
or not self.fields['public_key'].required):
|
||
|
return self.data.get('public_key')
|
||
|
KEY_ERROR_MESSAGE = _("Please input a proper SSH key")
|
||
|
openssh_pubkey_str = self.data.get('public_key').strip()
|
||
|
|
||
|
with tempfile.NamedTemporaryFile(delete=True) as tmp_public_key_file:
|
||
|
tmp_public_key_file.write(openssh_pubkey_str.encode('utf-8'))
|
||
|
tmp_public_key_file.flush()
|
||
|
try:
|
||
|
subprocess.check_output(
|
||
|
['ssh-keygen', '-lf', tmp_public_key_file.name])
|
||
|
except subprocess.CalledProcessError as cpe:
|
||
|
logger.debug(
|
||
|
"Not a correct ssh format {error}".format(error=str(cpe)))
|
||
|
raise forms.ValidationError(KEY_ERROR_MESSAGE)
|
||
|
return xml.sax.saxutils.escape(openssh_pubkey_str)
|
||
|
|
||
|
def clean_name(self):
|
||
|
INVALID_NAME_MESSAGE = _("Comma not accepted in the name of the key")
|
||
|
if "," in self.data.get('name'):
|
||
|
logger.debug(INVALID_NAME_MESSAGE)
|
||
|
raise forms.ValidationError(INVALID_NAME_MESSAGE)
|
||
|
return self.data.get('name')
|
||
|
|
||
|
def clean_user(self):
|
||
|
return self.request.user if self.request.user.is_authenticated else None
|
||
|
|
||
|
def clean(self):
|
||
|
cleaned_data = self.cleaned_data
|
||
|
if 'generate' in self.request.POST:
|
||
|
self.cleaned_data['name'] = generate_ssh_key_name()
|
||
|
private_key, public_key = UserHostingKey.generate_keys()
|
||
|
cleaned_data.update({
|
||
|
'private_key': private_key,
|
||
|
'public_key': public_key
|
||
|
})
|
||
|
|
||
|
return cleaned_data
|
||
|
|
||
|
class Meta:
|
||
|
model = UserHostingKey
|
||
|
fields = ['user', 'name', 'public_key']
|
||
|
|