diff options
Diffstat (limited to 'django_authopenid')
-rw-r--r-- | django_authopenid/external_login.py | 103 | ||||
-rw-r--r-- | django_authopenid/forms.py | 462 | ||||
-rw-r--r-- | django_authopenid/models.py | 5 | ||||
-rw-r--r-- | django_authopenid/urls.py | 13 | ||||
-rw-r--r-- | django_authopenid/util.py | 7 | ||||
-rw-r--r-- | django_authopenid/views.py | 342 |
6 files changed, 588 insertions, 344 deletions
diff --git a/django_authopenid/external_login.py b/django_authopenid/external_login.py new file mode 100644 index 00000000..bd49c009 --- /dev/null +++ b/django_authopenid/external_login.py @@ -0,0 +1,103 @@ +#this file contains stub functions that can be extended to support +#connect legacy login with external site +import settings +from django_authopenid.models import ExternalLoginData +import httplib +import urllib +import Cookie +import cookielib +from django import forms +import xml.dom.minidom as xml +import logging + +def login(request,user): + """performs the additional external login operation + """ + pass + +def set_login_cookies(response,user): + #should be unique value by design + try: + eld = ExternalLoginData.objects.get(user=user) + + data = eld.external_session_data + dom = xml.parseString(data) + login_response = dom.getElementsByTagName('login')[0] + userid = login_response.getAttribute('lguserid') + username = login_response.getAttribute('lgusername') + token = login_response.getAttribute('lgtoken') + prefix = login_response.getAttribute('cookieprefix').decode('utf-8') + sessionid = login_response.getAttribute('sessionid') + + c = {} + c[prefix + 'UserName'] = username + c[prefix + 'UserID'] = userid + c[prefix + 'Token'] = token + c[prefix + '_session'] = sessionid + + #custom code that copies cookies from external site + #not sure how to set paths and domain of cookies here + for key in c: + if c[key]: + response.set_cookie(str(key),value=str(c[key])) + except ExternalLoginData.DoesNotExist: + #this must be an OpenID login + pass + +#function to perform external logout, if needed +def logout(request): + pass + +#should raise User.DoesNotExist or pass +def clean_username(username): + return username + +def check_password(username,password): + """connects to external site and submits username/password pair + return True or False depending on correctness of login + saves remote unique id and remote session data in table ExternalLoginData + may raise forms.ValidationError + """ + host = settings.EXTERNAL_LEGACY_LOGIN_HOST + port = settings.EXTERNAL_LEGACY_LOGIN_PORT + ext_site = httplib.HTTPConnection(host,port) + + #custom code. this one does authentication through + #MediaWiki API + params = urllib.urlencode({'action':'login','format':'xml', + 'lgname':username,'lgpassword':password}) + headers = {"Content-type": "application/x-www-form-urlencoded", + 'User-Agent':"User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7", + "Accept": "text/xml"} + ext_site.request("POST","/wiki/api.php",params,headers) + response = ext_site.getresponse() + if response.status != 200: + raise forms.ValidationError('error ' + response.status + ' ' + response.reason) + data = response.read().strip() + ext_site.close() + + dom = xml.parseString(data) + login = dom.getElementsByTagName('login')[0] + result = login.getAttribute('result') + + if result == 'Success': + username = login.getAttribute('lgusername') + try: + eld = ExternalLoginData.objects.get(external_username=username) + except ExternalLoginData.DoesNotExist: + eld = ExternalLoginData() + eld.external_username = username + eld.external_session_data = data + eld.save() + return True + else: + error = login.getAttribute('details') + raise forms.ValidationError(error) + return False + +def createuser(username,email,password): + pass + +#retrieve email address +def get_email(username,password): + return '' diff --git a/django_authopenid/forms.py b/django_authopenid/forms.py index 690a781f..d4482751 100644 --- a/django_authopenid/forms.py +++ b/django_authopenid/forms.py @@ -35,8 +35,10 @@ 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 external_login +import types import re +from django.utils.safestring import mark_safe # needed for some linux distributions like debian @@ -46,16 +48,110 @@ except ImportError: from yadis import xri from django_authopenid.util import clean_next +from django_authopenid.models import ExternalLoginData + +__all__ = ['OpenidSigninForm', 'ClassicLoginForm', 'OpenidVerifyForm', + 'OpenidRegisterForm', 'ClassicRegisterForm', 'ChangePasswordForm', + 'ChangeEmailForm', 'EmailPasswordForm', 'DeleteForm', + 'ChangeOpenidForm'] -__all__ = ['OpenidSigninForm', 'OpenidAuthForm', 'OpenidVerifyForm', - 'OpenidRegisterForm', 'RegistrationForm', 'ChangepwForm', - 'ChangeemailForm', 'EmailPasswordForm', 'DeleteForm', - 'ChangeOpenidForm', 'ChangeEmailForm', 'ChangepwForm'] +class NextUrlField(forms.CharField): + def __init__(self): + super(NextUrlField,self).__init__(max_length = 255,widget = forms.HiddenInput(),required = False) + def clean(self,value): + return clean_next(value) + +attrs_dict = { 'class': 'required login' } + +class UserNameField(forms.CharField): + 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') + def __init__(self,must_exist=False,skip_clean=False,label=_('choose a username'),**kw): + self.must_exist = must_exist + self.skip_clean = skip_clean + super(UserNameField,self).__init__(max_length=30, + widget=forms.TextInput(attrs=attrs_dict), + label=label, + error_messages={'required':_('user name is required'), + 'taken':_('sorry, this name is taken, please choose another'), + 'forbidden':_('sorry, this name is not allowed, please choose another'), + 'missing':_('sorry, there is no user with this name'), + 'multiple-taken':_('sorry, we have a serious error - user name is taken by several users'), + 'invalid':_('user name can only consist of letters, empty space and underscore'), + }, + **kw + ) + + def clean(self,username): + """ validate username """ + username = super(UserNameField,self).clean(username.strip()) + if self.skip_clean == True: + return username + if not username_re.search(username): + raise forms.ValidationError(self.error_messages['invalid']) + if username in self.RESERVED_NAMES: + raise forms.ValidationError(self.error_messages['forbidden']) + try: + user = User.objects.get( + username__exact = username + ) + if user: + if self.must_exist: + return username + else: + raise forms.ValidationError(self.error_messages['taken']) + except User.DoesNotExist: + if self.must_exist: + raise forms.ValidationError(self.error_messages['missing']) + else: + return username + except User.MultipleObjectsReturned: + raise forms.ValidationError(self.error_messages['multiple-taken']) + +class UserEmailField(forms.EmailField): + def __init__(self,skip_clean=False,**kw): + self.skip_clean = skip_clean + super(UserEmailField,self).__init__(widget=forms.TextInput(attrs=dict(attrs_dict, + maxlength=200)), label=mark_safe(_('your email address')), + error_messages={'required':_('email address is required'), + 'invalid':_('please enter a valid email address'), + 'taken':_('this email is already used by someone else, please choose another'), + }, + **kw + ) + + def clean(self,email): + """ validate if email exist in database + from legacy register + return: raise error if it exist """ + email = super(UserEmailField,self).clean(email.strip()) + if self.skip_clean: + return email + if settings.EMAIL_UNIQUE == True: + try: + user = User.objects.get(email = email) + raise forms.ValidationError(self.error_messsages['taken']) + except User.DoesNotExist: + return email + except User.MultipleObjectsReturned: + raise forms.ValidationError(self.error_messages['taken']) + else: + return email + +def clean_nonempty_field_method(self,field): + value = None + if field in self.cleaned_data: + value = str(self.cleaned_data[field]).strip() + if value == '': + value = None + self.cleaned_data[field] = value + return value 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) + next = NextUrlField() def clean_openid_url(self): """ test if openid is accepted """ @@ -67,76 +163,98 @@ class OpenidSigninForm(forms.Form): 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): +class ClassicLoginForm(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)) + next = NextUrlField() + username = UserNameField(required=False,skip_clean=True) password = forms.CharField(max_length=128, - widget=forms.widgets.PasswordInput(attrs=attrs_dict)) - + widget=forms.widgets.PasswordInput(attrs=attrs_dict), required=False) + def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None): - super(OpenidAuthForm, self).__init__(data, files, auto_id, + super(ClassicLoginForm, self).__init__(data, files, auto_id, prefix, initial) self.user_cache = None - + + clean_nonempty_field = clean_nonempty_field_method + 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'] + return self.clean_nonempty_field('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'] - ) + return self.clean_nonempty_field('password') + + def clean(self): + """ + this clean function actuall cleans username and password + + test if password is valid for this username + this is really the "authenticate" function + also openid_auth is not an authentication backend + since it's written in a way that does not comply with + the Django convention + """ + + error_list = [] + username = self.cleaned_data['username'] + password = self.cleaned_data['password'] + + self.user_cache = None + if username and password: + + if settings.USE_EXTERNAL_LEGACY_LOGIN == True: + pw_ok = False + try: + pw_ok = external_login.check_password(username,password) + except forms.ValidationError, e: + error_list.extend(e.messages) + if pw_ok: + external_user = ExternalLoginData.objects.get(external_username=username) + if external_user.user == None: + return self.cleaned_data + user = external_user.user + openid_logins = user.userassociation_set.all() + + if len(openid_logins) > 0: + msg1 = _('Account with this name already exists on the forum') + msg2 = _('can\'t have two logins to the same account yet, sorry.') + error_list.append(msg1) + error_list.append(msg2) + self._errors['__all__'] = forms.util.ErrorList(error_list) + return self.cleaned_data + else: + #synchronize password with external login + user.set_password(password) + user.save() + #this auth will always succeed + self.user_cache = authenticate(username=user.username,\ + password=password) + else: + #keep self.user_cache == None + #nothing to do, error message will be set below + pass + else: + self.user_cache = authenticate(username=username, password=password) + if self.user_cache is None: - raise forms.ValidationError(_("Please enter a valid \ - username and password. Note that both fields are \ - case-sensitive.")) + del self.cleaned_data['username'] + del self.cleaned_data['password'] + error_list.insert(0,(_("Please enter valid username and password " + "(both are case-sensitive)."))) elif self.user_cache.is_active == False: - raise forms.ValidationError(_("This account is inactive.")) - return self.cleaned_data['password'] + error_list.append(_("This account is inactive.")) + if len(error_list) > 0: + error_list.insert(0,_('Login failed.')) + elif password == None and username == None: + error_list.append(_('Please enter username and password')) + elif password == None: + error_list.append(_('Please enter your password')) + elif username == None: + error_list.append(_('Please enter user name')) + if len(error_list) > 0: + self._errors['__all__'] = forms.util.ErrorList(error_list) + return self.cleaned_data - 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 @@ -144,56 +262,14 @@ class OpenidAuthForm(forms.Form): 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']) < settings.MIN_USERNAME_LENGTH: - 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): - """Optionally, for security reason one unique email in database""" - if 'email' in self.cleaned_data: - if settings.EMAIL_UNIQUE == True: - 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.")) - else: - return self.cleaned_data['email'] - #what if not??? - + next = NextUrlField() + username = UserNameField() + email = UserEmailField() + 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)) + next = NextUrlField() + username = UserNameField(must_exist=True) password = forms.CharField(max_length=128, widget=forms.widgets.PasswordInput(attrs=attrs_dict)) @@ -203,24 +279,6 @@ class OpenidVerifyForm(forms.Form): 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(_('invalid user name')) - 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 \ @@ -236,7 +294,7 @@ class OpenidVerifyForm(forms.Form): 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 @@ -245,88 +303,54 @@ class OpenidVerifyForm(forms.Form): attrs_dict = { 'class': 'required' } username_re = re.compile(r'^[\w ]+$') -class RegistrationForm(forms.Form): +class ClassicRegisterForm(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')) + next = NextUrlField() + username = UserNameField() + email = UserEmailField() password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict), - label=_('choose password')) + label=_('choose password'), + error_messages={'required':_('password is required')}, + ) 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: - if settings.EMAIL_UNIQUE == True: - 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.') - else: - return self.cleaned_data['email'] - #what if not? + label=mark_safe(_('retype password')), + error_messages={'required':_('please, retype your password'), + 'nomatch':_('sorry, entered passwords did not match, please try again')}, + required=False + ) 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'] = self.cleaned_data.get('password2','') + if self.cleaned_data['password2'] == '': + del self.cleaned_data['password2'] + raise forms.ValidationError(self.fields['password2'].error_messages['required']) + if 'password1' 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') + else: + del self.cleaned_data['password2'] + del self.cleaned_data['password1'] + raise forms.ValidationError(self.fields['password2'].error_messages['nomatch']) - -class ChangepwForm(forms.Form): +class ChangePasswordForm(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)) + oldpw = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict), + label=mark_safe(_('Current password'))) + password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict), + label=mark_safe(_('New password'))) + password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict), + label=mark_safe(_('Retype new password'))) 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) + super(ChangePasswordForm, self).__init__(data, *args, **kwargs) self.user = user def clean_oldpw(self): @@ -347,49 +371,35 @@ class ChangepwForm(forms.Form): raise forms.ValidationError(_("new passwords do not match")) -class ChangeemailForm(forms.Form): +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)) + email = UserEmailField(skip_clean=True) 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, + 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 settings.EMAIL_UNIQUE == True: - if self.user.email != self.cleaned_data['email']: - try: - user = User.objects.get(email = self.cleaned_data['email']) - except User.DoesNotExist: + try: + user = User.objects.get(email = self.cleaned_data['email']) + if self.user and self.user == user: 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.') + 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.') else: return self.cleaned_data['email'] - #what if not? - - 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, @@ -422,8 +432,7 @@ class DeleteForm(forms.Form): class EmailPasswordForm(forms.Form): """ send new password form """ - username = forms.CharField(max_length=30, - widget=forms.TextInput(attrs={'class': "required" })) + username = UserNameField(skip_clean=True,label=mark_safe(_('Your user name (<i>required</i>)'))) def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None): @@ -431,7 +440,6 @@ class EmailPasswordForm(forms.Form): prefix, initial) self.user_cache = None - def clean_username(self): """ get user for this username """ if 'username' in self.cleaned_data: diff --git a/django_authopenid/models.py b/django_authopenid/models.py index e6fb8111..7b2e1c02 100644 --- a/django_authopenid/models.py +++ b/django_authopenid/models.py @@ -69,3 +69,8 @@ class UserPasswordQueue(models.Model): def __unicode__(self): return self.user.username + +class ExternalLoginData(models.Model): + external_username = models.CharField(max_length=40, unique=True, null=False) + external_session_data = models.TextField() + user = models.ForeignKey(User, null=True) diff --git a/django_authopenid/urls.py b/django_authopenid/urls.py index 3382434a..112cbbe1 100644 --- a/django_authopenid/urls.py +++ b/django_authopenid/urls.py @@ -7,11 +7,12 @@ urlpatterns = patterns('django_authopenid.views', url(r'^yadis.xrdf$', 'xrdf', name='yadis_xrdf'), # manage account registration url(r'^%s$' % _('signin/'), 'signin', name='user_signin'), - url(r'^%s%s$' % (_('signin/'),_('newquestion/')), 'signin', kwargs = {'newquestion':True}), - url(r'^%s%s$' % (_('signin/'),_('newanswer/')), 'signin', kwargs = {'newanswer':True}), + url(r'^%s%s$' % (_('signin/'),_('newquestion/')), 'signin', kwargs = {'newquestion':True}, name='user_signin_new_question'), + url(r'^%s%s$' % (_('signin/'),_('newanswer/')), 'signin', kwargs = {'newanswer':True}, name='user_signin_new_answer'), url(r'^%s$' % _('signout/'), 'signout', name='user_signout'), url(r'^%s%s$' % (_('signin/'), _('complete/')), 'complete_signin', name='user_complete_signin'), + url('^%s$' % _('external-login/'),'external_legacy_login_info', name='user_external_legacy_login_issues'), url(r'^%s$' % _('register/'), 'register', name='user_register'), url(r'^%s$' % _('signup/'), 'signup', name='user_signup'), #disable current sendpw function @@ -21,8 +22,10 @@ urlpatterns = patterns('django_authopenid.views', # manage account settings url(r'^$', _('account_settings'), name='user_account_settings'), url(r'^%s$' % _('password/'), 'changepw', name='user_changepw'), - url(r'^%s$' % _('email/'), 'changeemail', name='user_changeemail',kwargs = {'action':'change'}), - url(r'^%s%s$' % (_('email/'),_('validate/')), 'changeemail', name='user_changeemail',kwargs = {'action':'validate'}), - #url(r'^%s$' % _('openid/'), 'changeopenid', name='user_changeopenid'), + url(r'^%s%s$' % (_('email/'),_('validate/')), 'changeemail', name='user_validateemail',kwargs = {'action':'validate'}), + url(r'^%s%s$' % (_('email/'), _('change/')), 'changeemail', name='user_changeemail'), + url(r'^%s%s$' % (_('email/'), _('sendkey/')), 'send_email_key', name='send_email_key'), + url(r'^%s%s(?P<id>\d+)/(?P<key>[\dabcdef]{32})/$' % (_('email/'), _('verify/')), 'verifyemail', name='user_verifyemail'), + url(r'^%s$' % _('openid/'), 'changeopenid', name='user_changeopenid'), url(r'^%s$' % _('delete/'), 'delete', name='user_delete'), ) diff --git a/django_authopenid/util.py b/django_authopenid/util.py index 11afe53b..2b9d44a2 100644 --- a/django_authopenid/util.py +++ b/django_authopenid/util.py @@ -7,7 +7,7 @@ import openid.store from django.db.models.query import Q from django.conf import settings from django.http import str_to_unicode - +from django.core.urlresolvers import reverse # needed for some linux distributions like debian try: @@ -22,7 +22,7 @@ from models import Association, Nonce __all__ = ['OpenID', 'DjangoOpenIDStore', 'from_openid_response', 'clean_next'] -DEFAULT_NEXT = getattr(settings, 'OPENID_REDIRECT_NEXT', '/') +DEFAULT_NEXT = '/' + getattr(settings, 'FORUM_SCRIPT_ALIAS') def clean_next(next): if next is None: return DEFAULT_NEXT @@ -32,6 +32,9 @@ def clean_next(next): return next return DEFAULT_NEXT +def get_next_url(request): + return clean_next(request.REQUEST.get('next')) + class OpenID: def __init__(self, openid_, issued, attrs=None, sreg_=None): self.openid = openid_ diff --git a/django_authopenid/views.py b/django_authopenid/views.py index fa94c4e5..feb6b58f 100644 --- a/django_authopenid/views.py +++ b/django_authopenid/views.py @@ -30,19 +30,20 @@ # (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.http import HttpResponseRedirect, get_host, Http404 +from django.http import HttpResponseRedirect, get_host, Http404, \ + HttpResponseServerError from django.shortcuts import render_to_response as render from django.template import RequestContext, loader, Context from django.conf import settings from django.contrib.auth.models import User -from django.contrib.auth import logout #for login I've added wrapper below - called login from django.contrib.auth.decorators import login_required +from django.contrib.auth import authenticate from django.core.urlresolvers import reverse from django.utils.encoding import smart_unicode from django.utils.html import escape from django.utils.translation import ugettext as _ -from django.contrib.sites.models import Site from django.utils.http import urlquote_plus +from django.utils.safestring import mark_safe from django.core.mail import send_mail from django.views.defaults import server_error @@ -60,15 +61,24 @@ import re import urllib -from django_authopenid.util import OpenID, DjangoOpenIDStore, from_openid_response, clean_next -from django_authopenid.models import UserAssociation, UserPasswordQueue -from django_authopenid.forms import OpenidSigninForm, OpenidAuthForm, OpenidRegisterForm, \ - OpenidVerifyForm, RegistrationForm, ChangepwForm, ChangeemailForm, \ +from forum.forms import EditUserEmailFeedsForm +from django_authopenid.util import OpenID, DjangoOpenIDStore, from_openid_response, get_next_url +from django_authopenid.models import UserAssociation, UserPasswordQueue, ExternalLoginData +from django_authopenid.forms import OpenidSigninForm, ClassicLoginForm, OpenidRegisterForm, \ + OpenidVerifyForm, ClassicRegisterForm, ChangePasswordForm, ChangeEmailForm, \ ChangeopenidForm, DeleteForm, EmailPasswordForm +import external_login +import logging def login(request,user): from django.contrib.auth import login as _login from forum.models import user_logged_in #custom signal + + print 'in login call' + + if settings.USE_EXTERNAL_LEGACY_LOGIN == True: + external_login.login(request,user) + #1) get old session key session_key = request.session.session_key #2) login and get new session key @@ -76,6 +86,12 @@ def login(request,user): #3) send signal with old session key as argument user_logged_in.send(user=user,session_key=session_key,sender=None) +def logout(request): + from django.contrib.auth import logout as _logout#for login I've added wrapper below - called login + _logout(request) + if settings.USE_EXTERNAL_LEGACY_LOGIN == True: + external_login.logout(request) + def get_url_host(request): if request.is_secure(): protocol = 'https' @@ -138,7 +154,7 @@ def complete(request, on_success=None, on_failure=None, return_to=None): def default_on_success(request, identity_url, openid_response): """ default action on openid signin success """ request.session['openid'] = from_openid_response(openid_response) - return HttpResponseRedirect(clean_next(request.GET.get('next'))) + return HttpResponseRedirect(get_next_url(request)) def default_on_failure(request, message): """ default failure action on signin """ @@ -152,16 +168,15 @@ def not_authenticated(func): he is already logged.""" def decorated(request, *args, **kwargs): if request.user.is_authenticated(): - next = request.GET.get("next", "/") - return HttpResponseRedirect(next) + return HttpResponseRedirect(get_next_url(request)) return func(request, *args, **kwargs) return decorated @not_authenticated def signin(request,newquestion=False,newanswer=False): """ - signin page. It manage the legacy authentification (user/password) - and authentification with openid. + signin page. It manages the legacy authentification (user/password) + and openid authentification url: /signin/ @@ -169,27 +184,112 @@ def signin(request,newquestion=False,newanswer=False): """ request.encoding = 'UTF-8' on_failure = signin_failure - next = clean_next(request.GET.get('next')) - + email_feeds_form = EditUserEmailFeedsForm() + next = get_next_url(request) form_signin = OpenidSigninForm(initial={'next':next}) - form_auth = OpenidAuthForm(initial={'next':next}) + form_auth = ClassicLoginForm(initial={'next':next}) if request.POST: - + #'blogin' - password login if 'blogin' in request.POST.keys(): - # perform normal django authentification - form_auth = OpenidAuthForm(request.POST) + form_auth = ClassicLoginForm(request.POST) if form_auth.is_valid(): - user_ = form_auth.get_user() - login(request, user_) - next = clean_next(form_auth.cleaned_data.get('next')) - print 'next stop is "%s"' % next - return HttpResponseRedirect(next) + #have login and password and need to login through external website + if settings.USE_EXTERNAL_LEGACY_LOGIN == True: + username = form_auth.cleaned_data['username'] + password = form_auth.cleaned_data['password'] + next = form_auth.cleaned_data['next'] + if form_auth.get_user() == None: + #need to create internal user + + #1) save login and password temporarily in session + request.session['external_username'] = username + request.session['external_password'] = password + + #2) see if username clashes with some existing user + #if so, we have to prompt the user to pick a different name + username_taken = User.is_username_taken(username) + #try: + # User.objects.get(username=username) + # username_taken = True + #except User.DoesNotExist: + # username_taken = False + + #3) try to extract user email from external service + email = external_login.get_email(username,password) + + email_feeds_form = EditUserEmailFeedsForm() + form_data = {'username':username,'email':email,'next':next} + form = OpenidRegisterForm(initial=form_data) + template_data = {'form1':form,'username':username,\ + 'email_feeds_form':email_feeds_form,\ + 'provider':mark_safe(settings.EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME),\ + 'login_type':'legacy',\ + 'gravatar_faq_url':reverse('faq') + '#gravatar',\ + 'external_login_name_is_taken':username_taken} + return render('authopenid/complete.html',template_data,\ + context_instance=RequestContext(request)) + else: + #user existed, external password is ok + user = form_auth.get_user() + login(request,user) + response = HttpResponseRedirect(get_next_url(request)) + external_login.set_login_cookies(response,user) + return response + else: + #regular password authentication + user = form_auth.get_user() + login(request, user) + return HttpResponseRedirect(get_next_url(request)) + + elif 'bnewaccount' in request.POST.keys(): + #register externally logged in password user with a new local account + if settings.USE_EXTERNAL_LEGACY_LOGIN == True: + form = OpenidRegisterForm(request.POST) + email_feeds_form = EditUserEmailFeedsForm(request.POST) + form1_is_valid = form.is_valid() + form2_is_valid = email_feeds_form.is_valid() + if form1_is_valid and form2_is_valid: + #create the user + username = form.cleaned_data['username'] + password = request.session.get('external_password',None) + email = form.cleaned_data['email'] + print 'got email addr %s' % email + if password and username: + User.objects.create_user(username,email,password) + user = authenticate(username=username,password=password) + external_username = request.session['external_username'] + eld = ExternalLoginData.objects.get(external_username=external_username) + eld.user = user + eld.save() + login(request,user) + email_feeds_form.save(user) + del request.session['external_username'] + del request.session['external_password'] + return HttpResponseRedirect(reverse('index')) + else: + if password: + del request.session['external_username'] + if username: + del request.session['external_password'] + return HttpResponseServerError() + else: + username = request.POST.get('username',None) + provider = mark_safe(settings.EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME) + username_taken = User.is_username_taken(username) + data = {'login_type':'legacy','form1':form,'username':username,\ + 'email_feeds_form':email_feeds_form,'provider':provider,\ + 'gravatar_faq_url':reverse('faq') + '#gravatar',\ + 'external_login_name_is_taken':username_taken} + return render('authopenid/complete.html',data, + context_instance=RequestContext(request)) + else: + raise Http404 elif 'bsignin' in request.POST.keys() or 'openid_username' in request.POST.keys(): form_signin = OpenidSigninForm(request.POST) if form_signin.is_valid(): - next = clean_next(form_signin.cleaned_data.get('next')) + next = form_signin.cleaned_data['next'] sreg_req = sreg.SRegRequest(optional=['nickname', 'email']) redirect_to = "%s%s?%s" % ( get_url_host(request), @@ -203,6 +303,7 @@ def signin(request,newquestion=False,newanswer=False): sreg_request=sreg_req) + #if request is GET question = None if newquestion == True: from forum.models import AnonymousQuestion as AQ @@ -232,7 +333,6 @@ def complete_signin(request): return complete(request, signin_success, signin_failure, get_url_host(request) + reverse('user_complete_signin')) - def signin_success(request, identity_url, openid_response): """ openid signin success. @@ -256,8 +356,7 @@ def signin_success(request, identity_url, openid_response): user_.backend = "django.contrib.auth.backends.ModelBackend" login(request, user_) - next = clean_next(request.GET.get('next')) - return HttpResponseRedirect(next) + return HttpResponseRedirect(get_next_url(request)) def is_association_exist(openid_url): """ test if an openid is already in database """ @@ -284,15 +383,13 @@ def register(request): template : authopenid/complete.html """ - is_redirect = False - next = clean_next(request.GET.get('next')) openid_ = request.session.get('openid', None) + next = get_next_url(request) if not openid_: - return HttpResponseRedirect(reverse('user_signin') + next) + return HttpResponseRedirect(reverse('user_signin') + '?next=%s' % next) nickname = openid_.sreg.get('nickname', '') email = openid_.sreg.get('email', '') - form1 = OpenidRegisterForm(initial={ 'next': next, 'username': nickname, @@ -302,18 +399,22 @@ def register(request): 'next': next, 'username': nickname, }) + email_feeds_form = EditUserEmailFeedsForm() user_ = None + is_redirect = False if request.POST: - just_completed = False if 'bnewaccount' in request.POST.keys(): form1 = OpenidRegisterForm(request.POST) - if form1.is_valid(): - next = clean_next(form1.cleaned_data.get('next')) + email_feeds_form = EditUserEmailFeedsForm(request.POST) + if form1.is_valid() and email_feeds_form.is_valid(): + next = form1.cleaned_data['next'] is_redirect = True tmp_pwd = User.objects.make_random_password() user_ = User.objects.create_user(form1.cleaned_data['username'], form1.cleaned_data['email'], tmp_pwd) + + user_.set_unusable_password() # make association with openid uassoc = UserAssociation(openid_url=str(openid_), user_id=user_.id) @@ -322,11 +423,12 @@ def register(request): # login user_.backend = "django.contrib.auth.backends.ModelBackend" login(request, user_) + email_feeds_form.save(user_) elif 'bverify' in request.POST.keys(): form2 = OpenidVerifyForm(request.POST) if form2.is_valid(): is_redirect = True - next = clean_next(form2.cleaned_data.get('next')) + next = form2.cleaned_data['next'] user_ = form2.get_user() uassoc = UserAssociation(openid_url=str(openid_), @@ -338,13 +440,16 @@ def register(request): #this needs to be a function call becase this is also done #if user just logged in and did not need to create the new account - if user_ != None and settings.EMAIL_VALIDATION == 'on': - send_new_email_key(user_,nomessage=True) - output = validation_email_sent(request) - set_email_validation_message(user_) #message set after generating view - return output - elif user_ != None and user_.is_authenticated(): - return HttpResponseRedirect('/') + if user_ != None: + if settings.EMAIL_VALIDATION == 'on': + send_new_email_key(user_,nomessage=True) + output = validation_email_sent(request) + set_email_validation_message(user_) #message set after generating view + return output + if user_.is_authenticated(): + return HttpResponseRedirect(reverse('index')) + else: + raise Exception('openid login failed')#should not ever get here openid_str = str(openid_) bits = openid_str.split('/') @@ -366,9 +471,12 @@ def register(request): return render('authopenid/complete.html', { 'form1': form1, 'form2': form2, - 'provider':provider_logo, - 'nickname': nickname, - 'email': email + 'email_feeds_form': email_feeds_form, + 'provider':mark_safe(provider_logo), + 'username': nickname, + 'email': email, + 'login_type':'openid', + 'gravatar_faq_url':reverse('faq') + '#gravatar', }, context_instance=RequestContext(request)) def signin_failure(request, message): @@ -377,9 +485,9 @@ def signin_failure(request, message): template : "authopenid/signin.html" """ - next = clean_next(request.GET.get('next')) + next = get_next_url(request) form_signin = OpenidSigninForm(initial={'next': next}) - form_auth = OpenidAuthForm(initial={'next': next}) + form_auth = ClassicLoginForm(initial={'next': next}) return render('authopenid/signin.html', { 'msg': message, @@ -396,42 +504,52 @@ def signup(request): templates: authopenid/signup.html, authopenid/confirm_email.txt """ - action_signin = reverse('user_signin') - next = clean_next(request.GET.get('next')) - form = RegistrationForm(initial={'next':next}) - form_signin = OpenidSigninForm(initial={'next':next}) - + if settings.USE_EXTERNAL_LEGACY_LOGIN == True: + return HttpResponseRedirect(reverse('user_external_legacy_login_issues')) + next = get_next_url(request) if request.POST: - form = RegistrationForm(request.POST) - if form.is_valid(): - next = clean_next(form.cleaned_data.get('next')) - user_ = User.objects.create_user( form.cleaned_data['username'], - form.cleaned_data['email'], form.cleaned_data['password1']) + form = ClassicRegisterForm(request.POST) + email_feeds_form = EditUserEmailFeedsForm(request.POST) + + #validation outside if to remember form values + form1_is_valid = form.is_valid() + form2_is_valid = email_feeds_form.is_valid() + if form1_is_valid and form2_is_valid: + next = form.cleaned_data['next'] + username = form.cleaned_data['username'] + password = form.cleaned_data['password1'] + email = form.cleaned_data['email'] + + user_ = User.objects.create_user( username,email,password ) + if settings.USE_EXTERNAL_LEGACY_LOGIN == True: + external_login.create_user(username,email,password) user_.backend = "django.contrib.auth.backends.ModelBackend" login(request, user_) + email_feeds_form.save(user_) # send email - current_domain = Site.objects.get_current().domain - subject = _("Welcome") + subject = _("Welcome email subject line") message_template = loader.get_template( 'authopenid/confirm_email.txt' ) message_context = Context({ - 'site_url': 'http://%s/' % current_domain, - 'username': form.cleaned_data['username'], - 'password': form.cleaned_data['password1'] + 'signup_url': settings.APP_URL + reverse('user_signin'), + 'username': username, + 'password': password, }) message = message_template.render(message_context) send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user_.email]) - return HttpResponseRedirect(next) - + else: + form = ClassicRegisterForm(initial={'next':next}) + email_feeds_form = EditUserEmailFeedsForm() return render('authopenid/signup.html', { - 'form': form, - 'form2': form_signin, + 'form': form, + 'email_feeds_form': email_feeds_form }, context_instance=RequestContext(request)) + #what if request is not posted? @login_required def signout(request): @@ -444,10 +562,8 @@ def signout(request): del request.session['openid'] except KeyError: pass - next = clean_next(request.GET.get('next')) logout(request) - - return HttpResponseRedirect(next) + return HttpResponseRedirect(get_next_url(request)) def xrdf(request): url_host = get_url_host(request) @@ -495,11 +611,16 @@ def changepw(request): url : /changepw/ template: authopenid/changepw.html """ - user_ = request.user + + if user_.has_usable_password(): + if settings.USE_EXTERNAL_LEGACY_LOGIN == True: + return HttpResponseRedirect(reverse('user_external_legacy_login_issues')) + else: + raise Http404 if request.POST: - form = ChangepwForm(request.POST, user=user_) + form = ChangePasswordForm(request.POST, user=user_) if form.is_valid(): user_.set_password(form.cleaned_data['password1']) user_.save() @@ -509,18 +630,20 @@ def changepw(request): urlquote_plus(msg)) return HttpResponseRedirect(redirect) else: - form = ChangepwForm(user=user_) + form = ChangePasswordForm(user=user_) return render('authopenid/changepw.html', {'form': form }, context_instance=RequestContext(request)) def find_email_validation_messages(user): - msg_text = _('your email needs to be validated') + msg_text = _('your email needs to be validated see %(details_url)s') \ + % {'details_url':reverse('faq') + '#validate'} return user.message_set.filter(message__exact=msg_text) def set_email_validation_message(user): messages = find_email_validation_messages(user) - msg_text = _('your email needs to be validated') + msg_text = _('your email needs to be validated see %(details_url)s') \ + % {'details_url':reverse('faq') + '#validate'} if len(messages) == 0: user.message_set.create(message=msg_text) @@ -540,11 +663,11 @@ def _send_email_key(user): """private function. sends email containing validation key to user's email address """ - subject = _("Welcome") + subject = _("Email verification subject line") message_template = loader.get_template('authopenid/email_validation.txt') import settings message_context = Context({ - 'validation_link': '%s/email/verify/%d/%s/' % (settings.APP_URL ,user.id,user.email_key) + 'validation_link': settings.APP_URL + reverse('user_verifyemail', kwargs={'id':user.id,'key':user.email_key}) }) message = message_template.render(message_context) send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email]) @@ -580,7 +703,7 @@ def send_email_key(request): context_instance=RequestContext(request) ) else: - _send_email_key(request.user) + send_new_email_key(request.user) return validation_email_sent(request) else: raise Http404 @@ -589,10 +712,11 @@ def send_email_key(request): #internal server view used as return value by other views def validation_email_sent(request): return render('authopenid/changeemail.html', - { 'email': request.user.email, 'action_type': 'validate', }, + { 'email': request.user.email, + 'change_email_url': reverse('user_changeemail'), + 'action_type': 'validate', }, context_instance=RequestContext(request)) - def verifyemail(request,id=None,key=None): """ view that is shown when user clicks email validation link @@ -611,53 +735,49 @@ def verifyemail(request,id=None,key=None): raise Http404 @login_required -def changeemail(request): +def changeemail(request, action='change'): """ - changeemail view. It require password or openid to allow change. + changeemail view. requires openid with request type GET url: /email/* template : authopenid/changeemail.html """ - msg = request.GET.get('msg', '') + msg = request.GET.get('msg', None) extension_args = {} user_ = request.user - redirect_to = get_url_host(request) + reverse('user_changeemail') - action = 'change' - if request.POST: - form = ChangeemailForm(request.POST, user=user_) + if 'cancel' in request.POST: + msg = _('your email was not changed') + request.user.message_set.create(message=msg) + return HttpResponseRedirect(get_next_url(request)) + form = ChangeEmailForm(request.POST, user=user_) if form.is_valid(): - if not form.test_openid: - new_email = form.cleaned_data['email'] - if new_email != user_.email: - if settings.EMAIL_VALIDATION == 'on': - action = 'validate' - else: - action = 'done_novalidate' - set_new_email(user_, new_email,nomessage=True) + new_email = form.cleaned_data['email'] + if new_email != user_.email: + if settings.EMAIL_VALIDATION == 'on': + action = 'validate' else: - action = 'keep' + action = 'done_novalidate' + set_new_email(user_, new_email,nomessage=True) else: - #what does this branch do? - return server_error('') - request.session['new_email'] = form.cleaned_data['email'] - return ask_openid(request, form.cleaned_data['password'], - redirect_to, on_failure=emailopenid_failure) + action = 'keep' elif not request.POST and 'openid.mode' in request.GET: + redirect_to = get_url_host(request) + reverse('user_changeemail') return complete(request, emailopenid_success, emailopenid_failure, redirect_to) else: - form = ChangeemailForm(initial={'email': user_.email}, + form = ChangeEmailForm(initial={'email': user_.email}, user=user_) - output = render('authopenid/changeemail.html', { 'form': form, 'email': user_.email, 'action_type': action, + 'gravatar_faq_url': reverse('faq') + '#gravatar', + 'change_email_url': reverse('user_changeemail'), 'msg': msg }, context_instance=RequestContext(request)) @@ -666,7 +786,6 @@ def changeemail(request): return output - def emailopenid_success(request, identity_url, openid_response): openid_ = from_openid_response(openid_response) @@ -840,7 +959,7 @@ def deleteopenid_success(request, identity_url, openid_response): identity_url)) msg = _("Account deleted.") - redirect = "/?msg=%s" % (urlquote_plus(msg)) + redirect = reverse('index') + u"/?msg=%s" % (urlquote_plus(msg)) return HttpResponseRedirect(redirect) @@ -848,6 +967,8 @@ def deleteopenid_failure(request, message): redirect_to = "%s?msg=%s" % (reverse('user_delete'), urlquote_plus(message)) return HttpResponseRedirect(redirect_to) +def external_legacy_login_info(request): + return render('authopenid/external_legacy_login_info.html', context_instance=RequestContext(request)) def sendpw(request): """ @@ -859,6 +980,8 @@ def sendpw(request): templates : authopenid/sendpw_email.txt, authopenid/sendpw.html """ + if settings.USE_EXTERNAL_LEGACY_LOGIN == True: + return HttpResponseRedirect(reverse('user_external_legacy_login_issues')) msg = request.GET.get('msg','') if request.POST: @@ -878,21 +1001,20 @@ def sendpw(request): uqueue.confirm_key = confirm_key uqueue.save() # send email - current_domain = Site.objects.get_current().domain subject = _("Request for new password") message_template = loader.get_template( 'authopenid/sendpw_email.txt') + key_link = settings.APP_URL + reverse('user_confirmchangepw') + '?key=' + confirm_key message_context = Context({ - 'site_url': 'http://%s' % current_domain, - 'confirm_key': confirm_key, + 'site_url': settings.APP_URL + reverse('index'), + 'key_link': key_link, 'username': form.user_cache.username, 'password': new_pw, - 'url_confirm': reverse('user_confirmchangepw'), }) message = message_template.render(message_context) send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [form.user_cache.email]) - msg = _("A new password has been sent to your email address.") + msg = _("A new password and the activation link were sent to your email address.") else: form = EmailPasswordForm() @@ -915,7 +1037,7 @@ def confirmchangepw(request): """ confirm_key = request.GET.get('key', '') if not confirm_key: - return HttpResponseRedirect('/') + return HttpResponseRedirect(reverse('index')) try: uqueue = UserPasswordQueue.objects.get( |