diff options
Diffstat (limited to 'django_authopenid/forms.py')
-rw-r--r-- | django_authopenid/forms.py | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/django_authopenid/forms.py b/django_authopenid/forms.py new file mode 100644 index 00000000..09fa76b1 --- /dev/null +++ b/django_authopenid/forms.py @@ -0,0 +1,435 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2007, 2008, BenoƮt Chesneau +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# * notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# * notice, this list of conditions and the following disclaimer in the +# * documentation and/or other materials provided with the +# * distribution. Neither the name of the <ORGANIZATION> nor the names +# * of its contributors may be used to endorse or promote products +# * derived from this software without specific prior written +# * permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +from django import forms +from django.contrib.auth.models import User +from django.contrib.auth import authenticate +from django.utils.translation import ugettext as _ +from django.conf import settings + +import re + + +# needed for some linux distributions like debian +try: + from openid.yadis import xri +except ImportError: + from yadis import xri + +from django_authopenid.util import clean_next + +__all__ = ['OpenidSigninForm', 'OpenidAuthForm', 'OpenidVerifyForm', + 'OpenidRegisterForm', 'RegistrationForm', 'ChangepwForm', + 'ChangeemailForm', 'EmailPasswordForm', 'DeleteForm', + 'ChangeOpenidForm', 'ChangeEmailForm', 'ChangepwForm'] + +class OpenidSigninForm(forms.Form): + """ signin form """ + openid_url = forms.CharField(max_length=255, widget=forms.widgets.TextInput(attrs={'class': 'openid-login-input', 'size':80})) + next = forms.CharField(max_length=255, widget=forms.HiddenInput(), required=False) + + def clean_openid_url(self): + """ test if openid is accepted """ + if 'openid_url' in self.cleaned_data: + openid_url = self.cleaned_data['openid_url'] + if xri.identifierScheme(openid_url) == 'XRI' and getattr( + settings, 'OPENID_DISALLOW_INAMES', False + ): + raise forms.ValidationError(_('i-names are not supported')) + return self.cleaned_data['openid_url'] + + def clean_next(self): + """ validate next """ + if 'next' in self.cleaned_data and self.cleaned_data['next'] != "": + self.cleaned_data['next'] = clean_next(self.cleaned_data['next']) + return self.cleaned_data['next'] + + +attrs_dict = { 'class': 'required login' } +username_re = re.compile(r'^\w+$') +RESERVED_NAMES = (u'fuck', u'shit', u'ass', u'sex', u'add', + u'edit', u'save', u'delete', u'manage', u'update', 'remove', 'new') + +class OpenidAuthForm(forms.Form): + """ legacy account signin form """ + next = forms.CharField(max_length=255, widget=forms.HiddenInput(), + required=False) + username = forms.CharField(max_length=30, + widget=forms.widgets.TextInput(attrs=attrs_dict)) + password = forms.CharField(max_length=128, + widget=forms.widgets.PasswordInput(attrs=attrs_dict)) + + def __init__(self, data=None, files=None, auto_id='id_%s', + prefix=None, initial=None): + super(OpenidAuthForm, self).__init__(data, files, auto_id, + prefix, initial) + self.user_cache = None + + def clean_username(self): + """ validate username and test if it exists.""" + if 'username' in self.cleaned_data and \ + 'openid_url' not in self.cleaned_data: + if not username_re.search(self.cleaned_data['username']): + raise forms.ValidationError(_("Usernames can only contain \ + letters, numbers and underscores")) + try: + user = User.objects.get( + username__exact = self.cleaned_data['username'] + ) + except User.DoesNotExist: + raise forms.ValidationError(_("This username does not exist \ + in our database. Please choose another.")) + except User.MultipleObjectsReturned: + raise forms.ValidationError(u'There is already more than one \ + account registered with that username. Please try \ + another.') + return self.cleaned_data['username'] + + def clean_password(self): + """" test if password is valid for this username """ + if 'username' in self.cleaned_data and \ + 'password' in self.cleaned_data: + self.user_cache = authenticate( + username=self.cleaned_data['username'], + password=self.cleaned_data['password'] + ) + if self.user_cache is None: + raise forms.ValidationError(_("Please enter a valid \ + username and password. Note that both fields are \ + case-sensitive.")) + elif self.user_cache.is_active == False: + raise forms.ValidationError(_("This account is inactive.")) + return self.cleaned_data['password'] + + def clean_next(self): + """ validate next url """ + if 'next' in self.cleaned_data and \ + self.cleaned_data['next'] != "": + self.cleaned_data['next'] = clean_next(self.cleaned_data['next']) + return self.cleaned_data['next'] + + def get_user(self): + """ get authenticated user """ + return self.user_cache + + +class OpenidRegisterForm(forms.Form): + """ openid signin form """ + next = forms.CharField(max_length=255, widget=forms.HiddenInput(), + required=False) + username = forms.CharField(max_length=30, + widget=forms.widgets.TextInput(attrs=attrs_dict)) + email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict, + maxlength=200)), label=u'Email address') + + def clean_username(self): + """ test if username is valid and exist in database """ + if 'username' in self.cleaned_data: + if not username_re.search(self.cleaned_data['username']): + raise forms.ValidationError(_('invalid user name')) + if self.cleaned_data['username'] in RESERVED_NAMES: + raise forms.ValidationError(_('sorry, this name can not be used, please try another')) + if len(self.cleaned_data['username']) < 3: + raise forms.ValidationError(_('username too short')) + try: + user = User.objects.get( + username__exact = self.cleaned_data['username'] + ) + except User.DoesNotExist: + return self.cleaned_data['username'] + except User.MultipleObjectsReturned: + raise forms.ValidationError(_('this name is already in use - please try anoter')) + raise forms.ValidationError(_('this name is already in use - please try anoter')) + + def clean_email(self): + """For security reason one unique email in database""" + if 'email' in self.cleaned_data: + try: + user = User.objects.get(email = self.cleaned_data['email']) + except User.DoesNotExist: + return self.cleaned_data['email'] + except User.MultipleObjectsReturned: + raise forms.ValidationError(u'There is already more than one \ + account registered with that e-mail address. Please try \ + another.') + raise forms.ValidationError(_("This email is already \ + registered in our database. Please choose another.")) + + +class OpenidVerifyForm(forms.Form): + """ openid verify form (associate an openid with an account) """ + next = forms.CharField(max_length=255, widget = forms.HiddenInput(), + required=False) + username = forms.CharField(max_length=30, + widget=forms.widgets.TextInput(attrs=attrs_dict)) + password = forms.CharField(max_length=128, + widget=forms.widgets.PasswordInput(attrs=attrs_dict)) + + def __init__(self, data=None, files=None, auto_id='id_%s', + prefix=None, initial=None): + super(OpenidVerifyForm, self).__init__(data, files, auto_id, + prefix, initial) + self.user_cache = None + + def clean_username(self): + """ validate username """ + if 'username' in self.cleaned_data: + if not username_re.search(self.cleaned_data['username']): + raise forms.ValidationError(_("Usernames can only contain \ + letters, numbers and underscores")) + try: + user = User.objects.get( + username__exact = self.cleaned_data['username'] + ) + except User.DoesNotExist: + raise forms.ValidationError(_("This username don't exist. \ + Please choose another.")) + except User.MultipleObjectsReturned: + raise forms.ValidationError(u'Somehow, that username is in \ + use for multiple accounts. Please contact us to get this \ + problem resolved.') + return self.cleaned_data['username'] + + def clean_password(self): + """ test if password is valid for this user """ + if 'username' in self.cleaned_data and \ + 'password' in self.cleaned_data: + self.user_cache = authenticate( + username = self.cleaned_data['username'], + password = self.cleaned_data['password'] + ) + if self.user_cache is None: + raise forms.ValidationError(_("Please enter a valid \ + username and password. Note that both fields are \ + case-sensitive.")) + elif self.user_cache.is_active == False: + raise forms.ValidationError(_("This account is inactive.")) + return self.cleaned_data['password'] + + def get_user(self): + """ get authenticated user """ + return self.user_cache + + +attrs_dict = { 'class': 'required' } +username_re = re.compile(r'^\w+$') + +class RegistrationForm(forms.Form): + """ legacy registration form """ + + next = forms.CharField(max_length=255, widget=forms.HiddenInput(), + required=False) + username = forms.CharField(max_length=30, + widget=forms.TextInput(attrs=attrs_dict), + label=_('choose a username')) + email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict, + maxlength=200)), label=_('your email address')) + password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict), + label=_('choose password')) + password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict), + label=_('retype password')) + + def clean_username(self): + """ + Validates that the username is alphanumeric and is not already + in use. + + """ + if 'username' in self.cleaned_data: + if not username_re.search(self.cleaned_data['username']): + raise forms.ValidationError(u'Usernames can only contain \ + letters, numbers and underscores') + try: + user = User.objects.get( + username__exact = self.cleaned_data['username'] + ) + + except User.DoesNotExist: + return self.cleaned_data['username'] + except User.MultipleObjectsReturned: + raise forms.ValidationError(u'Somehow, that username is in \ + use for multiple accounts. Please contact us to get this \ + problem resolved.') + raise forms.ValidationError(u'This username is already taken. \ + Please choose another.') + + def clean_email(self): + """ validate if email exist in database + :return: raise error if it exist """ + if 'email' in self.cleaned_data: + try: + user = User.objects.get(email = self.cleaned_data['email']) + except User.DoesNotExist: + return self.cleaned_data['email'] + except User.MultipleObjectsReturned: + raise forms.ValidationError(u'There is already more than one \ + account registered with that e-mail address. Please try \ + another.') + raise forms.ValidationError(u'This email is already registered \ + in our database. Please choose another.') + return self.cleaned_data['email'] + + def clean_password2(self): + """ + Validates that the two password inputs match. + + """ + if 'password1' in self.cleaned_data and \ + 'password2' in self.cleaned_data and \ + self.cleaned_data['password1'] == \ + self.cleaned_data['password2']: + return self.cleaned_data['password2'] + raise forms.ValidationError(u'You must type the same password each \ + time') + + +class ChangepwForm(forms.Form): + """ change password form """ + oldpw = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict)) + password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict)) + password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict)) + + def __init__(self, data=None, user=None, *args, **kwargs): + if user is None: + raise TypeError("Keyword argument 'user' must be supplied") + super(ChangepwForm, self).__init__(data, *args, **kwargs) + self.user = user + + def clean_oldpw(self): + """ test old password """ + if not self.user.check_password(self.cleaned_data['oldpw']): + raise forms.ValidationError(_("Old password is incorrect. \ + Please enter the correct password.")) + return self.cleaned_data['oldpw'] + + def clean_password2(self): + """ + Validates that the two password inputs match. + """ + if 'password1' in self.cleaned_data and \ + 'password2' in self.cleaned_data and \ + self.cleaned_data['password1'] == self.cleaned_data['password2']: + return self.cleaned_data['password2'] + raise forms.ValidationError(_("new passwords do not match")) + + +class ChangeemailForm(forms.Form): + """ change email form """ + email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict, + maxlength=200)), label=u'Email address') + password = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict)) + + def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, \ + initial=None, user=None): + if user is None: + raise TypeError("Keyword argument 'user' must be supplied") + super(ChangeemailForm, self).__init__(data, files, auto_id, + prefix, initial) + self.test_openid = False + self.user = user + + + def clean_email(self): + """ check if email don't exist """ + if 'email' in self.cleaned_data: + if self.user.email != self.cleaned_data['email']: + try: + user = User.objects.get(email = self.cleaned_data['email']) + except User.DoesNotExist: + return self.cleaned_data['email'] + except User.MultipleObjectsReturned: + raise forms.ValidationError(u'There is already more than one \ + account registered with that e-mail address. Please try \ + another.') + raise forms.ValidationError(u'This email is already registered \ + in our database. Please choose another.') + return self.cleaned_data['email'] + + + def clean_password(self): + """ check if we have to test a legacy account or not """ + if 'password' in self.cleaned_data: + if not self.user.check_password(self.cleaned_data['password']): + self.test_openid = True + return self.cleaned_data['password'] + +class ChangeopenidForm(forms.Form): + """ change openid form """ + openid_url = forms.CharField(max_length=255, + widget=forms.TextInput(attrs={'class': "required" })) + + def __init__(self, data=None, user=None, *args, **kwargs): + if user is None: + raise TypeError("Keyword argument 'user' must be supplied") + super(ChangeopenidForm, self).__init__(data, *args, **kwargs) + self.user = user + +class DeleteForm(forms.Form): + """ confirm form to delete an account """ + confirm = forms.CharField(widget=forms.CheckboxInput(attrs=attrs_dict)) + password = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict)) + + def __init__(self, data=None, files=None, auto_id='id_%s', + prefix=None, initial=None, user=None): + super(DeleteForm, self).__init__(data, files, auto_id, prefix, initial) + self.test_openid = False + self.user = user + + def clean_password(self): + """ check if we have to test a legacy account or not """ + if 'password' in self.cleaned_data: + if not self.user.check_password(self.cleaned_data['password']): + self.test_openid = True + return self.cleaned_data['password'] + + +class EmailPasswordForm(forms.Form): + """ send new password form """ + username = forms.CharField(max_length=30, + widget=forms.TextInput(attrs={'class': "required" })) + + def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, + initial=None): + super(EmailPasswordForm, self).__init__(data, files, auto_id, + prefix, initial) + self.user_cache = None + + + def clean_username(self): + """ get user for this username """ + if 'username' in self.cleaned_data: + try: + self.user_cache = User.objects.get( + username = self.cleaned_data['username']) + except: + raise forms.ValidationError(_("Incorrect username.")) + return self.cleaned_data['username'] |