From c301ab5ab960a4b11ec397a7519a37d40d2659e0 Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 28 Jan 2016 17:39:08 -0600 Subject: [PATCH 1/2] Added decorators to hide passwords for django error emails --- userena/views.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/userena/views.py b/userena/views.py index a06d7214..d7fe3038 100644 --- a/userena/views.py +++ b/userena/views.py @@ -4,6 +4,7 @@ from django.contrib.auth import get_user_model from django.contrib.auth.forms import PasswordChangeForm from django.contrib.auth.views import logout as Signout +from django.views.decorators.debug import sensitive_post_parameters from django.views.generic import TemplateView from django.views.generic.list import ListView from django.contrib import messages @@ -69,6 +70,8 @@ def get_queryset(self): queryset = profile_model.objects.get_visible_profiles(self.request.user).select_related() return queryset + +@sensitive_post_parameters('password1', 'password2') @secure_required def signup(request, signup_form=SignupForm, template_name='userena/signup_form.html', success_url=None, @@ -386,6 +389,8 @@ def disabled_account(request, username, template_name, extra_context=None): return ExtraContextTemplateView.as_view(template_name=template_name, extra_context=extra_context)(request) + +@sensitive_post_parameters('password') @secure_required def signin(request, auth_form=AuthenticationForm, template_name='userena/signin_form.html', @@ -563,6 +568,8 @@ def email_change(request, username, email_form=ChangeEmailForm, return ExtraContextTemplateView.as_view(template_name=template_name, extra_context=extra_context)(request) + +@sensitive_post_parameters('old_password', 'new_password1', 'new_password2') @secure_required @permission_required_or_403('change_user', (get_user_model(), 'username', 'username')) def password_change(request, username, template_name='userena/password_form.html', From 2f3521955886b278061efd3f3f910016832dc1c4 Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 12 Aug 2016 14:06:02 -0500 Subject: [PATCH 2/2] Added signup redirect url parameter This works the same as the signin redirect. If a user was trying to access a page and gets redirected to sign in or sign up, after succeeding they will be redirected from where they came. Since a user can only be redirected to either the signin or signup page, it is beyond the scope of this feature to pass the query parameter in the url to the other page when requested. Updated signup redirect to work like signin Added signup redirect for SIGNIN_AFTER_SIGNUP --- userena/forms.py | 7 ++- userena/managers.py | 4 +- userena/models.py | 7 +-- .../emails/activation_email_message.html | 2 +- .../emails/activation_email_message.txt | 2 +- userena/templates/userena/signup_form.html | 1 + userena/tests/tests_views.py | 26 ++++++++++- userena/views.py | 44 ++++++++++++++----- 8 files changed, 73 insertions(+), 20 deletions(-) diff --git a/userena/forms.py b/userena/forms.py index dff9c0bf..92faf1dd 100644 --- a/userena/forms.py +++ b/userena/forms.py @@ -45,6 +45,10 @@ class SignupForm(forms.Form): render_value=False), label=_("Repeat password")) + def __init__(self, redirect=None, *args, **kwargs): + self.redirect = redirect + super(SignupForm, self).__init__(*args, **kwargs) + def clean_username(self): """ Validate that the username is alphanumeric and is not already in use. @@ -94,7 +98,8 @@ def save(self): email, password, not userena_settings.USERENA_ACTIVATION_REQUIRED, - userena_settings.USERENA_ACTIVATION_REQUIRED) + userena_settings.USERENA_ACTIVATION_REQUIRED, + redirect=self.redirect) return new_user class SignupFormOnlyEmail(SignupForm): diff --git a/userena/managers.py b/userena/managers.py index a4d91c09..dfc25d86 100644 --- a/userena/managers.py +++ b/userena/managers.py @@ -36,7 +36,7 @@ class UserenaManager(UserManager): """ Extra functionality for the Userena model. """ def create_user(self, username, email, password, active=False, - send_email=True): + send_email=True, redirect=None): """ A simple wrapper that creates a new :class:`User`. @@ -78,7 +78,7 @@ def create_user(self, username, email, password, active=False, userena_profile = self.create_userena_profile(new_user) if send_email: - userena_profile.send_activation_email() + userena_profile.send_activation_email(redirect=redirect) return new_user diff --git a/userena/models.py b/userena/models.py index a23d60be..3e7c50ed 100644 --- a/userena/models.py +++ b/userena/models.py @@ -156,11 +156,11 @@ def activation_key_expired(self): return True return False - def send_activation_email(self): + def send_activation_email(self, redirect=None): """ Sends a activation email to the user. - This email is send when the user wants to activate their newly created + This email is sent when the user wants to activate their newly created user. """ @@ -169,7 +169,8 @@ def send_activation_email(self): 'protocol': get_protocol(), 'activation_days': userena_settings.USERENA_ACTIVATION_DAYS, 'activation_key': self.activation_key, - 'site': Site.objects.get_current()} + 'site': Site.objects.get_current(), + 'redirect': redirect} mailer = UserenaConfirmationMail(context=context) mailer.generate_mail("activation") diff --git a/userena/templates/userena/emails/activation_email_message.html b/userena/templates/userena/emails/activation_email_message.html index 9f50e037..c3fcb6ae 100644 --- a/userena/templates/userena/emails/activation_email_message.html +++ b/userena/templates/userena/emails/activation_email_message.html @@ -5,7 +5,7 @@ {% blocktrans with site.name as site %}

Thank you for signing up at {{ site }}.

{% endblocktrans %}

{% trans "To activate your account you should click on the link below:" %}
- {{ protocol }}://{{ site.domain }}{% url 'userena_activate' activation_key %} + {{ protocol }}://{{ site.domain }}{% url 'userena_activate' activation_key %}{% if redirect %}?next={{redirect}}{% endif %}

{% trans "Thanks for using our site!" %}
diff --git a/userena/templates/userena/emails/activation_email_message.txt b/userena/templates/userena/emails/activation_email_message.txt index 7bf5d631..e1a50888 100644 --- a/userena/templates/userena/emails/activation_email_message.txt +++ b/userena/templates/userena/emails/activation_email_message.txt @@ -5,7 +5,7 @@ {% trans "To activate your account you should click on the link below:" %} -{{ protocol }}://{{ site.domain }}{% url 'userena_activate' activation_key %} +{{ protocol }}://{{ site.domain }}{% url 'userena_activate' activation_key %}{% if redirect %}?next={{redirect}}{% endif %} {% trans "Thanks for using our site!" %} diff --git a/userena/templates/userena/signup_form.html b/userena/templates/userena/signup_form.html index f10283b6..e2bc01fd 100644 --- a/userena/templates/userena/signup_form.html +++ b/userena/templates/userena/signup_form.html @@ -25,5 +25,6 @@ {% endfor %} + {% if next %}{% endif %} {% endblock %} diff --git a/userena/tests/tests_views.py b/userena/tests/tests_views.py index 6860332a..53253370 100644 --- a/userena/tests/tests_views.py +++ b/userena/tests/tests_views.py @@ -1,7 +1,7 @@ import re from datetime import datetime, timedelta -from django.contrib.auth import get_user_model +from django.contrib.auth import get_user_model, REDIRECT_FIELD_NAME from django.core.urlresolvers import reverse from django.core import mail from django.contrib.auth.forms import PasswordChangeForm @@ -36,6 +36,30 @@ def test_valid_activation(self): user = User.objects.get(email='alice@example.com') self.assertTrue(user.is_active) + def test_activation_redirect(self): + """ A ``GET`` to the activation view with redirect parameter """ + + redirect = '/some/url/' + + # First, register an account. + self.client.post('%s?%s=%s' % (reverse('userena_signup'), REDIRECT_FIELD_NAME, redirect), + data={'username': 'alice', + 'email': 'alice@example.com', + 'password1': 'swordfish', + 'password2': 'swordfish', + 'tos': 'on'}) + user = User.objects.get(email='alice@example.com') + + # Send a GET request with the redirect parameter to the url + response = self.client.get( + '%s?%s=%s' % (reverse('userena_activate', kwargs={'activation_key': user.userena_signup.activation_key}), + REDIRECT_FIELD_NAME, redirect)) + + self.assertTrue(response.get('Location').endswith(redirect)) + + if hasattr(response, 'url'): + self.assertTrue(response.url.endswith(redirect)) + def test_activation_expired_retry(self): """ A ``GET`` to the activation view when activation link is expired """ # First, register an account. diff --git a/userena/views.py b/userena/views.py index d7fe3038..064411f0 100644 --- a/userena/views.py +++ b/userena/views.py @@ -75,13 +75,14 @@ def get_queryset(self): @secure_required def signup(request, signup_form=SignupForm, template_name='userena/signup_form.html', success_url=None, - extra_context=None): + extra_context=None, redirect_field_name=REDIRECT_FIELD_NAME): """ Signup of an account. Signup requiring a username, email and password. After signup a user gets an email with an activation link used to activate their account. After - successful signup redirects to ``success_url``. + successful signup redirects to the ``REDIRECT_FIELD_NAME`` query parameter + if provided, otherwise ``success_url``. :param signup_form: Form that will be used to sign a user. Defaults to userena's @@ -101,10 +102,16 @@ def signup(request, signup_form=SignupForm, context. Defaults to a dictionary with a ``form`` key containing the ``signup_form``. + :param redirect_field_name: + String containing the name of the field that contains a redirect url. + Defaults to ``next``. + **Context** ``form`` Form supplied by ``signup_form``. + ``next`` + Next supplied by a query parameter. """ # If signup is disabled, return 403 @@ -118,8 +125,11 @@ def signup(request, signup_form=SignupForm, form = signup_form() + redirect_url = request.GET.get(redirect_field_name, request.POST.get(redirect_field_name, None)) + if request.method == 'POST': - form = signup_form(request.POST, request.FILES) + form = signup_form(redirect=redirect_url, data=request.POST, files=request.FILES) + if form.is_valid(): user = form.save() @@ -127,7 +137,6 @@ def signup(request, signup_form=SignupForm, userena_signals.signup_complete.send(sender=None, user=user) - if success_url: redirect_to = success_url else: redirect_to = reverse('userena_signup_complete', kwargs={'username': user.username}) @@ -141,10 +150,18 @@ def signup(request, signup_form=SignupForm, user = authenticate(identification=user.email, check_password=False) login(request, user) + # since we signed in the user and have no more information to display, + # we can redirect the user to a requested page + if redirect_url: + redirect_to = redirect_url + return redirect(redirect_to) if not extra_context: extra_context = dict() - extra_context['form'] = form + extra_context.update({ + 'form': form, + 'next': redirect_url, + }) return ExtraContextTemplateView.as_view(template_name=template_name, extra_context=extra_context)(request) @@ -152,17 +169,19 @@ def signup(request, signup_form=SignupForm, def activate(request, activation_key, template_name='userena/activate_fail.html', retry_template_name='userena/activate_retry.html', - success_url=None, extra_context=None): + success_url=None, extra_context=None, + redirect_field_name=REDIRECT_FIELD_NAME): """ Activate a user with an activation key. The key is a SHA1 string. When the SHA1 is found with an :class:`UserenaSignup`, the :class:`User` of that account will be activated. After a successful activation the view will redirect to - ``success_url``. If the SHA1 is not found, the user will be shown the - ``template_name`` template displaying a fail message. - If the SHA1 is found but expired, ``retry_template_name`` is used instead, - so the user can proceed to :func:`activate_retry` to get a new activation key. + the ``REDIRECT_FIELD_NAME`` query parameter if provided, otherwise + ``success_url``. If the SHA1 is not found, the user will be shown the + ``template_name`` template displaying a fail message. If the SHA1 is found + but expired, ``retry_template_name`` is used instead, so the user can + proceed to :func:`activate_retry` to get a new activation key. :param activation_key: String of a SHA1 string of 40 characters long. A SHA1 is always 160bit @@ -204,7 +223,10 @@ def activate(request, activation_key, messages.success(request, _('Your account has been activated and you have been signed in.'), fail_silently=True) - if success_url: redirect_to = success_url % {'username': user.username } + redirect_to = request.GET.get(redirect_field_name, None) + + if redirect_to: pass + elif success_url: redirect_to = success_url % {'username': user.username } else: redirect_to = reverse('userena_profile_detail', kwargs={'username': user.username}) return redirect(redirect_to)