~ubuntuone-pqm-team/canonical-identity-provider/trunk

« back to all changes in this revision

Viewing changes to identityprovider/forms.py

  • Committer: Danny Tamez
  • Date: 2010-04-21 15:29:24 UTC
  • Revision ID: danny.tamez@canonical.com-20100421152924-lq1m92tstk2iz75a
Canonical SSO Provider (Open Source) - Initial Commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2010 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
import logging
 
5
 
 
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 _
 
11
 
 
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
 
19
 
 
20
logger = logging.getLogger('sso')
 
21
 
 
22
default_errors = {
 
23
    'required': _(u'Required field.'),
 
24
    'invalid': _(u'Invalid value.'),
 
25
}
 
26
 
 
27
email_errors = {
 
28
    'required': _(u'Required field.'),
 
29
    'invalid': _(u'Invalid email.'),
 
30
}
 
31
 
 
32
 
 
33
class GenericEmailForm(forms.Form):
 
34
    email = fields.EmailField(error_messages=email_errors,
 
35
        widget=widgets.TextInput(attrs={'class': 'textType', 'size': '26'}))
 
36
 
 
37
 
 
38
class LoginForm(GenericEmailForm):
 
39
    password = fields.CharField(error_messages=default_errors,
 
40
        widget=widgets.PasswordInput(attrs={'class': 'textType',
 
41
                                            'size': ' 26'}))
 
42
 
 
43
    def clean(self):
 
44
        if 'email' in self.cleaned_data and 'password' in self.cleaned_data:
 
45
            email = self.cleaned_data['email']
 
46
            verified = False
 
47
            try:
 
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:
 
52
                pass
 
53
 
 
54
            if not verified:
 
55
                raise forms.ValidationError(
 
56
                    mark_safe(_("Password didn't match.")))
 
57
 
 
58
            password = self.cleaned_data['password']
 
59
            try:
 
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.")))
 
65
 
 
66
            account = Account.objects.get_by_email(email)
 
67
            if not account.is_active:
 
68
                raise forms.ValidationError(_("Your account has been "
 
69
                    "deactivated"))
 
70
 
 
71
            if user is not None and user.is_active:
 
72
                    return self.cleaned_data
 
73
            else:
 
74
                raise forms.ValidationError(mark_safe(
 
75
                    _("Password didn't match.")))
 
76
 
 
77
 
 
78
class ForgotPasswordForm(GenericEmailForm):
 
79
    def clean_email(self):
 
80
        if 'email' in self.cleaned_data:
 
81
            email = self.cleaned_data['email']
 
82
            try:
 
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
 
88
                pass
 
89
            return email
 
90
 
 
91
 
 
92
class ResetPasswordForm(forms.Form):
 
93
    password = fields.CharField(error_messages=default_errors,
 
94
        widget=widgets.PasswordInput(attrs={
 
95
            'class': 'textType',
 
96
            'size': '20',
 
97
            'onfocus': 'togglePasswordPolicy(true)',
 
98
            'onblur': 'togglePasswordPolicy(false)'
 
99
        }))
 
100
    passwordconfirm = fields.CharField(error_messages=default_errors,
 
101
        widget=widgets.PasswordInput(attrs={
 
102
            'class': 'textType',
 
103
            'size': '20'
 
104
        }))
 
105
 
 
106
    def clean_password(self):
 
107
        if 'password' in self.cleaned_data:
 
108
            password = self.cleaned_data['password']
 
109
            try:
 
110
                str(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."))
 
118
            return password
 
119
 
 
120
    def clean(self):
 
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"))
 
127
        return cleaned_data
 
128
 
 
129
 
 
130
class NewAccountForm(GenericEmailForm):
 
131
    pass
 
132
 
 
133
 
 
134
class ConfirmNewAccountForm(ResetPasswordForm):
 
135
    displayname = fields.CharField(error_messages=default_errors,
 
136
        widget=widgets.TextInput(attrs={'class': 'textType', 'size': '20'}))
 
137
 
 
138
 
 
139
class PreferredEmailField(forms.ModelChoiceField):
 
140
    def label_from_instance(self, obj):
 
141
        return obj.email
 
142
 
 
143
 
 
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,
 
151
        empty_label=None)
 
152
    newpassword = fields.CharField(required=False,
 
153
        widget=widgets.PasswordInput(attrs={
 
154
            'class': 'disableAutoComplete textType',
 
155
            'size': '20',
 
156
            'onfocus': 'togglePasswordPolicy(true)',
 
157
            'onblur': 'togglePasswordPolicy(false)'
 
158
            }))
 
159
    newpasswordconfirm = fields.CharField(required=False,
 
160
        widget=widgets.PasswordInput(attrs={
 
161
            'class': 'disableAutoComplete textType',
 
162
            'size': '20'
 
163
        }))
 
164
 
 
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')
 
169
 
 
170
        if data is None:
 
171
            kwargs['initial'] = {'displayname': self.account.displayname}
 
172
 
 
173
        super(EditAccountForm, self).__init__(data, **kwargs)
 
174
 
 
175
        if self.account.preferredemail is None:
 
176
            preferredemail_id = None
 
177
        else:
 
178
            preferredemail_id = self.account.preferredemail.id
 
179
 
 
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,
 
187
            empty_label=None)
 
188
 
 
189
    def clean_displayname(self):
 
190
        name = self.cleaned_data['displayname'].strip()
 
191
        if len(name) == 0:
 
192
            raise forms.ValidationError(_("Required field"))
 
193
        return name
 
194
 
 
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."))
 
202
            return email
 
203
 
 
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"))
 
209
        if password:
 
210
            try:
 
211
                str(password)
 
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."))
 
219
        return password
 
220
 
 
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."))
 
226
        return password
 
227
 
 
228
    def clean(self):
 
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"))
 
236
        return cleaned_data
 
237
 
 
238
    def save_account(self):
 
239
        if self.is_valid():
 
240
            self.account.displayname = self.cleaned_data['displayname']
 
241
            self.account.preferredemail = self.cleaned_data['preferred_email']
 
242
            self.account.save()
 
243
            password = self.cleaned_data['newpassword']
 
244
            if password:
 
245
                password_obj = self.account.accountpassword
 
246
                password_obj.password = encrypt_launchpad_password(password)
 
247
                password_obj.save()
 
248
            return True
 
249
        return False
 
250
 
 
251
 
 
252
class NewEmailForm(forms.Form):
 
253
    newemail = fields.EmailField(error_messages=email_errors,
 
254
        widget=widgets.TextInput(attrs={'class': 'textType', 'size': '26'}))
 
255
 
 
256
    def clean_newemail(self):
 
257
        data = self.cleaned_data['newemail']
 
258
        try:
 
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:
 
263
            return data
 
264
        return data
 
265
 
 
266
 
 
267
class PreAuthorizeForm(forms.Form):
 
268
    trust_root = forms.CharField(error_messages=default_errors)
 
269
    callback = forms.CharField(error_messages=default_errors)