1
# Copyright 2010 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
6
from django import forms
7
from django.contrib import auth
8
from django.forms import fields, widgets
9
from django.utils.safestring import mark_safe
10
from django.utils.translation import ugettext as _
12
from identityprovider.models import Account, EmailAddress
13
from identityprovider.models.const import EmailStatus
14
from identityprovider.utils import (
15
get_person_and_account_by_email, CannotResetPasswordException,
16
PersonAndAccountNotFoundException, encrypt_launchpad_password,
17
password_policy_compliant)
18
from identityprovider.widgets import ROAwareTextInput, ROAwareSelect
20
logger = logging.getLogger('sso')
23
'required': _(u'Required field.'),
24
'invalid': _(u'Invalid value.'),
28
'required': _(u'Required field.'),
29
'invalid': _(u'Invalid email.'),
33
class GenericEmailForm(forms.Form):
34
email = fields.EmailField(error_messages=email_errors,
35
widget=widgets.TextInput(attrs={'class': 'textType', 'size': '26'}))
38
class LoginForm(GenericEmailForm):
39
password = fields.CharField(error_messages=default_errors,
40
widget=widgets.PasswordInput(attrs={'class': 'textType',
44
if 'email' in self.cleaned_data and 'password' in self.cleaned_data:
45
email = self.cleaned_data['email']
48
email_addr = EmailAddress.objects.get(email__iexact=email)
49
verified = email_addr.status in [EmailStatus.VALIDATED,
50
EmailStatus.OLD, EmailStatus.PREFERRED]
51
except EmailAddress.DoesNotExist:
55
raise forms.ValidationError(
56
mark_safe(_("Password didn't match.")))
58
password = self.cleaned_data['password']
60
user = auth.authenticate(username=email, password=password)
61
except UnicodeEncodeError:
62
# Password not ASCII, we must fail
63
raise forms.ValidationError(mark_safe(
64
_("Password didn't match.")))
66
account = Account.objects.get_by_email(email)
67
if not account.is_active:
68
raise forms.ValidationError(_("Your account has been "
71
if user is not None and user.is_active:
72
return self.cleaned_data
74
raise forms.ValidationError(mark_safe(
75
_("Password didn't match.")))
78
class ForgotPasswordForm(GenericEmailForm):
79
def clean_email(self):
80
if 'email' in self.cleaned_data:
81
email = self.cleaned_data['email']
83
person, account = get_person_and_account_by_email(email)
84
except (CannotResetPasswordException,
85
PersonAndAccountNotFoundException):
86
# Return the email. We do not raise a ValidationError,
87
# because we'd like to show the 'Email sent' page anyway
92
class ResetPasswordForm(forms.Form):
93
password = fields.CharField(error_messages=default_errors,
94
widget=widgets.PasswordInput(attrs={
97
'onfocus': 'togglePasswordPolicy(true)',
98
'onblur': 'togglePasswordPolicy(false)'
100
passwordconfirm = fields.CharField(error_messages=default_errors,
101
widget=widgets.PasswordInput(attrs={
106
def clean_password(self):
107
if 'password' in self.cleaned_data:
108
password = self.cleaned_data['password']
111
except UnicodeEncodeError:
112
raise forms.ValidationError(
113
_("Invalid characters in password"))
114
if not password_policy_compliant(password):
115
raise forms.ValidationError(_("Password must be at least "
116
"8 characters long, and must contain at least one "
117
"number and an upper case letter."))
121
cleaned_data = self.cleaned_data
122
password = cleaned_data.get('password')
123
passwordconfirm = cleaned_data.get('passwordconfirm')
124
if (password != passwordconfirm
125
and not self['password'].errors):
126
raise forms.ValidationError(_("Passwords didn't match"))
130
class NewAccountForm(GenericEmailForm):
134
class ConfirmNewAccountForm(ResetPasswordForm):
135
displayname = fields.CharField(error_messages=default_errors,
136
widget=widgets.TextInput(attrs={'class': 'textType', 'size': '20'}))
139
class PreferredEmailField(forms.ModelChoiceField):
140
def label_from_instance(self, obj):
144
class EditAccountForm(forms.Form):
145
displayname = fields.CharField(error_messages=default_errors,
146
widget=ROAwareTextInput(attrs={'class': 'textType', 'size': '20'}))
147
preferred_email = PreferredEmailField(
148
error_messages=email_errors,
149
queryset=EmailAddress.objects.none(),
150
widget=ROAwareSelect,
152
newpassword = fields.CharField(required=False,
153
widget=widgets.PasswordInput(attrs={
154
'class': 'disableAutoComplete textType',
156
'onfocus': 'togglePasswordPolicy(true)',
157
'onblur': 'togglePasswordPolicy(false)'
159
newpasswordconfirm = fields.CharField(required=False,
160
widget=widgets.PasswordInput(attrs={
161
'class': 'disableAutoComplete textType',
165
def __init__(self, data=None, **kwargs):
166
# These keyword arguments are required to render the form successfully
167
assert 'account' in kwargs, True
168
self.account = kwargs.pop('account')
171
kwargs['initial'] = {'displayname': self.account.displayname}
173
super(EditAccountForm, self).__init__(data, **kwargs)
175
if self.account.preferredemail is None:
176
preferredemail_id = None
178
preferredemail_id = self.account.preferredemail.id
180
# Override the field to set initial value (current preferred email)
181
self.fields['preferred_email'] = PreferredEmailField(
182
queryset=EmailAddress.objects.filter(account=self.account).\
183
exclude(status=EmailStatus.NEW).order_by('-status', 'email'),
184
initial=preferredemail_id,
185
widget=ROAwareSelect,
186
error_messages=email_errors,
189
def clean_displayname(self):
190
name = self.cleaned_data['displayname'].strip()
192
raise forms.ValidationError(_("Required field"))
195
def clean_preferred_email(self):
196
if 'preferred_email' in self.cleaned_data:
197
email = self.cleaned_data['preferred_email']
198
if email.status == EmailStatus.NEW:
199
logger.debug("status is NEW.")
200
raise forms.ValidationError(_("Please select an "
201
"already validated address."))
204
def clean_newpassword(self):
205
password = self.cleaned_data.get('newpassword')
206
if not password and (self.data.get('currentpassword') or
207
self.data.get('newpasswordconfirm')):
208
raise forms.ValidationError(_("Required field"))
212
except UnicodeEncodeError:
213
raise forms.ValidationError(
214
_("Invalid characters in password"))
215
if not password_policy_compliant(password):
216
raise forms.ValidationError(_("Password must be at least "
217
"8 characters long, and must contain at least one "
218
"number and an upper case letter."))
221
def clean_newpasswordconfirm(self):
222
password = self.cleaned_data.get('newpasswordconfirm')
223
if not password and (self.data.get('currentpassword') or
224
self.data.get('newpassword')):
225
raise forms.ValidationError(_("Required field."))
229
cleaned_data = self.cleaned_data
230
newpassword = cleaned_data.get('newpassword')
231
newpasswordconfirm = cleaned_data.get('newpasswordconfirm')
232
if newpassword or newpasswordconfirm:
233
if (newpassword != newpasswordconfirm and
234
not self['newpasswordconfirm'].errors):
235
raise forms.ValidationError(_("Didn't match"))
238
def save_account(self):
240
self.account.displayname = self.cleaned_data['displayname']
241
self.account.preferredemail = self.cleaned_data['preferred_email']
243
password = self.cleaned_data['newpassword']
245
password_obj = self.account.accountpassword
246
password_obj.password = encrypt_launchpad_password(password)
252
class NewEmailForm(forms.Form):
253
newemail = fields.EmailField(error_messages=email_errors,
254
widget=widgets.TextInput(attrs={'class': 'textType', 'size': '26'}))
256
def clean_newemail(self):
257
data = self.cleaned_data['newemail']
259
email = EmailAddress.objects.get(email__iexact=data)
260
if email.status != EmailStatus.NEW:
261
raise forms.ValidationError(_("Email already registered."))
262
except EmailAddress.DoesNotExist:
267
class PreAuthorizeForm(forms.Form):
268
trust_root = forms.CharField(error_messages=default_errors)
269
callback = forms.CharField(error_messages=default_errors)