From: melinath Date: Tue, 17 Aug 2010 16:37:37 +0000 (-0400) Subject: Implemented EmailTokenGenerator and added password changing to the LoginMultiView. X-Git-Tag: philo-0.9~34^2~2^2~1 X-Git-Url: http://git.ithinksw.org/philo.git/commitdiff_plain/e9c5d3eb331486b87d7276d37b86933bd810200b Implemented EmailTokenGenerator and added password changing to the LoginMultiView. --- diff --git a/contrib/waldo/models.py b/contrib/waldo/models.py index 3436505..4f0cd8c 100644 --- a/contrib/waldo/models.py +++ b/contrib/waldo/models.py @@ -17,7 +17,7 @@ from django.views.decorators.cache import never_cache from django.views.decorators.csrf import csrf_protect from philo.models import MultiView, Page from philo.contrib.waldo.forms import LOGIN_FORM_KEY, LoginForm, RegistrationForm -from philo.contrib.waldo.tokens import registration_token_generator +from philo.contrib.waldo.tokens import registration_token_generator, email_token_generator import urlparse @@ -41,6 +41,7 @@ class LoginMultiView(MultiView): password_reset_page = models.ForeignKey(Page, related_name='%(app_label)s_%(class)s_password_reset_related') password_reset_confirmation_email = models.ForeignKey(Page, related_name='%(app_label)s_%(class)s_password_reset_confirmation_email_related') password_set_page = models.ForeignKey(Page, related_name='%(app_label)s_%(class)s_password_set_related') + password_change_page = models.ForeignKey(Page, related_name='%(app_label)s_%(class)s_password_change_related', blank=True, null=True) register_page = models.ForeignKey(Page, related_name='%(app_label)s_%(class)s_register_related') register_confirmation_email = models.ForeignKey(Page, related_name='%(app_label)s_%(class)s_register_confirmation_email_related') @@ -48,18 +49,20 @@ class LoginMultiView(MultiView): def urlpatterns(self): urlpatterns = patterns('', url(r'^login/$', self.login, name='login'), - url(r'^logout/$', self.logout, name='logout') - ) - urlpatterns += patterns('', + url(r'^logout/$', self.logout, name='logout'), + url(r'^password/reset/$', csrf_protect(self.password_reset), name='password_reset'), - url(r'^password/reset/(?P\w+)/(?P[^/]+)/$', - self.password_reset_confirm, name='password_reset_confirm') - ) - urlpatterns += patterns('', + url(r'^password/reset/(?P\w+)/(?P[^/]+)/$', self.password_reset_confirm, name='password_reset_confirm'), + url(r'^register/$', csrf_protect(self.register), name='register'), - url(r'^register/(?P\w+)/(?P[^/]+)/$', - self.register_confirm, name='register_confirm') + url(r'^register/(?P\w+)/(?P[^/]+)/$', self.register_confirm, name='register_confirm') ) + + if self.password_change_page: + urlpatterns += patterns('', + url(r'^password/change/$', csrf_protect(self.login_required(self.password_change)), name='password_change'), + ) + return urlpatterns def get_context(self, extra_dict=None): @@ -186,7 +189,7 @@ class LoginMultiView(MultiView): 'username': user.username } self.send_confirmation_email('Confirm password reset for account at %s' % current_site.domain, user.email, self.password_reset_confirmation_email, context) - messages.add_message(request, messages.SUCCESS, "An email has been sent to the address you provided with details on resetting your password.") + messages.add_message(request, messages.SUCCESS, "An email has been sent to the address you provided with details on resetting your password.", fail_silently=True) return HttpResponseRedirect('') else: form = PasswordResetForm() @@ -224,6 +227,20 @@ class LoginMultiView(MultiView): raise Http404 + def password_change(self, request, node=None, extra_context=None): + if request.method == 'POST': + form = PasswordChangeForm(request.user, request.POST) + if form.is_valid(): + form.save() + messages.add_message(request, messages.SUCCESS, 'Password changed successfully.', fail_silently=True) + return HttpResponseRedirect('') + else: + form = PasswordChangeForm(request.user) + + context = self.get_context({'form': form}) + context.update(extra_context or {}) + return self.password_change_page.render_to_response(node, request, extra_context=context) + def register(self, request, node=None, extra_context=None, token_generator=registration_token_generator): if request.user.is_authenticated(): return HttpResponseRedirect(node.get_absolute_url()) @@ -239,7 +256,7 @@ class LoginMultiView(MultiView): 'link': link } self.send_confirmation_email('Confirm account creation at %s' % current_site.name, user.email, self.register_confirmation_email, context) - messages.add_message(request, messages.SUCCESS, 'An email has been sent to %s with details on activating your account.' % user.email) + messages.add_message(request, messages.SUCCESS, 'An email has been sent to %s with details on activating your account.' % user.email, fail_silently=True) return HttpResponseRedirect('') else: form = RegistrationForm() @@ -369,7 +386,7 @@ class AccountMultiView(LoginMultiView): def account_required(self, view): def inner(request, *args, **kwargs): if not self.has_valid_account(request.user): - messages.add_message(request, messages.ERROR, "You need to add some account information before you can post listings.") + messages.add_message(request, messages.ERROR, "You need to add some account information before you can post listings.", fail_silently=True) return self.account_view(request, *args, **kwargs) return view(request, *args, **kwargs) @@ -377,7 +394,7 @@ class AccountMultiView(LoginMultiView): return inner def post_register_confirm_redirect(self, request, node): - messages.add_message(request, messages.INFO, 'Welcome! Please fill in some more information.') + messages.add_message(request, messages.INFO, 'Welcome! Please fill in some more information.', fail_silently=True) return HttpResponseRedirect('/%s/%s/' % (node.get_absolute_url().strip('/'), reverse('account', urlconf=self).strip('/'))) class Meta: diff --git a/contrib/waldo/tokens.py b/contrib/waldo/tokens.py index fe32274..95ce0c0 100644 --- a/contrib/waldo/tokens.py +++ b/contrib/waldo/tokens.py @@ -10,20 +10,13 @@ from django.contrib.auth.tokens import PasswordResetTokenGenerator REGISTRATION_TIMEOUT_DAYS = getattr(settings, 'WALDO_REGISTRATION_TIMEOUT_DAYS', 1) +EMAIL_TIMEOUT_DAYS = getattr(settings, 'WALDO_EMAIL_TIMEOUT_DAYS', 1) class RegistrationTokenGenerator(PasswordResetTokenGenerator): """ Strategy object used to generate and check tokens for the user registration mechanism. """ - def make_token(self, user): - """ - Returns a token that can be used once to activate a user's account. - """ - if user.is_active: - return False - return self._make_token_with_timestamp(user, self._num_days(self._today())) - def check_token(self, user, token): """ Check that a registration token is correct for a given user. @@ -63,4 +56,51 @@ class RegistrationTokenGenerator(PasswordResetTokenGenerator): hash = sha_constructor(settings.SECRET_KEY + unicode(user.id) + unicode(user.is_active) + user.last_login.strftime('%Y-%m-%d %H:%M:%S') + unicode(timestamp)).hexdigest()[::2] return '%s-%s' % (ts_b36, hash) -registration_token_generator = RegistrationTokenGenerator() \ No newline at end of file + +registration_token_generator = RegistrationTokenGenerator() + + +class EmailTokenGenerator(PasswordResetTokenGenerator): + """ + Strategy object used to generate and check tokens for a user email change mechanism. + """ + def make_token(self, user, email): + """ + Returns a token that can be used once to do an email change for the given user and email. + """ + return self._make_token_with_timestamp(user, email, self._num_days(self._today())) + + def check_token(self, user, email, token): + if email == user.email: + return False + + # Parse the token + try: + ts_b36, hash = token.split('-') + except ValueError: + return False + + try: + ts = base36_to_int(ts_b36) + except ValueError: + return False + + # Check that the timestamp and uid have not been tampered with. + if self._make_token_with_timestamp(user, email, ts) != token: + return False + + # Check that the timestamp is within limit + if (self._num_days(self._today()) - ts) > EMAIL_TIMEOUT_DAYS: + return False + + return True + + def _make_token_with_timestamp(self, user, email, timestamp): + ts_b36 = int_to_base36(timestamp) + + from django.utils.hashcompat import sha_constructor + hash = sha_constructor(settings.SECRET_KEY + unicode(user.id) + user.email + email + unicode(timestamp)).hexdigest()[::2] + return '%s-%s' % (ts_b36, hash) + + +email_token_generator = EmailTokenGenerator() \ No newline at end of file