diff --git a/basetheme_bootstrap/shortcuts.py b/basetheme_bootstrap/shortcuts.py new file mode 100644 index 0000000000000000000000000000000000000000..67932dbbc8cc3214c81e2c70e16e7e543ef7d036 --- /dev/null +++ b/basetheme_bootstrap/shortcuts.py @@ -0,0 +1,7 @@ +from django.shortcuts import redirect +from urllib.parse import urlparse + + +def redirect_same_domain(to, *args, permanent=False, **kwargs): + to = urlparse(to).path + return redirect(to, *args, permanent=permanent, **kwargs) diff --git a/basetheme_bootstrap/tests.py b/basetheme_bootstrap/tests.py index e32dcb07c5c2d76da4483013a815d80226d9052e..7bfa7d2959138696ef42509c9380b955ccd8f163 100644 --- a/basetheme_bootstrap/tests.py +++ b/basetheme_bootstrap/tests.py @@ -56,6 +56,48 @@ class SignUpTests(TestCase): self.assertEqual(user.email, data["email"]) +class SignUpAttackWithNextParamTests(TestCase): + + def setUp(self): + cache.clear() + + def test_legit_redirect(self): + user_count = get_user_model().objects.count() + data = { + 'username': "userAAA", + 'email': "userAAA@mp.com", + 'password1': "user@mp.comuser@mp.comuser@mp.comuser@mp.com", + 'password2': "user@mp.comuser@mp.comuser@mp.comuser@mp.com", + 'first_name': "user", + 'next': reverse('basetheme_bootstrap:account'), + } + response = self.client.post(reverse('basetheme_bootstrap:signup'), data, follow=False) + self.assertEqual(response.status_code, 302) + self.assertRedirects(response, expected_url=reverse('basetheme_bootstrap:account'), ) + self.assertEqual(get_user_model().objects.count(), user_count + 1) + user = get_user_model().objects.last() + self.assertEqual(user.username, data["username"]) + self.assertEqual(user.email, data["email"]) + + def test_attacking_redirect(self): + user_count = get_user_model().objects.count() + data = { + 'username': "userAAA", + 'email': "userAAA@mp.com", + 'password1': "user@mp.comuser@mp.comuser@mp.comuser@mp.com", + 'password2': "user@mp.comuser@mp.comuser@mp.comuser@mp.com", + 'first_name': "user", + 'next': "https://www.attacking.com" + reverse('basetheme_bootstrap:account'), + } + response = self.client.post(reverse('basetheme_bootstrap:signup'), data, follow=False) + self.assertEqual(response.status_code, 302) + self.assertRedirects(response, expected_url=reverse('basetheme_bootstrap:account'), ) + self.assertEqual(get_user_model().objects.count(), user_count + 1) + user = get_user_model().objects.last() + self.assertEqual(user.username, data["username"]) + self.assertEqual(user.email, data["email"]) + + @override_settings( BASETHEME_BOOTSTRAP_USERNAME_IS_EMAIL=True, ) @@ -143,7 +185,7 @@ class SignUpWithFirstLastNameRequiredTests(TestCase): @override_settings( BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION=True, PASSWORD_RESET_TIMEOUT_DAYS=1, - PASSWORD_RESET_TIMEOUT=60*60*24, + PASSWORD_RESET_TIMEOUT=60 * 60 * 24, ) class SignUpWithValidationTests(TestCase): @@ -179,7 +221,7 @@ class SignUpWithValidationTests(TestCase): self.assertNotIn(Group.objects.get(name="PendingAccountUser"), user.groups.all()) ## test an account cannot be re-activated with the link: - user.is_active=False + user.is_active = False user.save() self.client.post(reverse('basetheme_bootstrap:activate', args=[m[0][0], m[0][1] + "-" + m[0][2]])) self.assertFalse(get_user_model().objects.last().is_active) @@ -260,11 +302,11 @@ class TestWithTemplatesInPlace(SignUpTests): @override_settings( - BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION=True, + BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION=True, ) class SuperuserSignUpTests(TestCase): def test_sign_up_for_superuser(self): - #even with BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION to True first user must be super and active + # even with BASETHEME_BOOTSTRAP_VALIDATE_EMAIL_BEFORE_ACTIVATION to True first user must be super and active response = self.client.post(reverse('basetheme_bootstrap:signup'), { 'username': "userAAA", 'email': "bryan.brancotte@pasteur.fr", @@ -272,7 +314,7 @@ class SuperuserSignUpTests(TestCase): 'password2': "user@mp.comuser@mp.comuser@mp.comuser@mp.com", 'first_name': "user" }) - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 302) self.assertTrue(get_user_model().objects.get(username="userAAA").is_superuser) self.assertTrue(get_user_model().objects.get(username="userAAA").is_active) diff --git a/basetheme_bootstrap/views.py b/basetheme_bootstrap/views.py index ef56a2006c76c83e9a46269b88271e731ed4ed12..8e1bf1a990f8e37050085c1f361c913bfd17f398 100644 --- a/basetheme_bootstrap/views.py +++ b/basetheme_bootstrap/views.py @@ -14,7 +14,7 @@ from django.core.mail import send_mail from django.db.models import ProtectedError from django.forms import widgets from django.http import HttpResponseForbidden -from django.shortcuts import render, redirect +from django.shortcuts import render from django.template import TemplateDoesNotExist from django.template.defaultfilters import time as time_filter from django.urls import reverse @@ -27,6 +27,7 @@ from basetheme_bootstrap import user_preferences_utils from basetheme_bootstrap.default_settings import is_validating_email from basetheme_bootstrap.forms import UserCreationFormWithMore, \ MyUserChangeForm, UserDeleteForm +from basetheme_bootstrap.shortcuts import redirect_same_domain as redirect logger = logging.getLogger(__name__) diff --git a/setup.cfg b/setup.cfg index c82f7d7507144e4f13f39d73b32b92596ce02cd6..4e0e67cd1d9519ca903795e6972c88bee1c2dd69 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = django-basetheme-bootstrap -version = 1.6.1 +version = 1.7.0 description = Django Basetheme Bootstrap long_description = file: README.rst url = https://gitlab.pasteur.fr/bbrancot/django-basetheme-bootstrap