From de7160a0e6b1b24d6854911a1e6b91e438902afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Fri, 25 Aug 2017 12:46:21 +0300 Subject: [PATCH] login, set-password, and change-email tokens use different salts. --- hc/accounts/backends.py | 3 +-- hc/accounts/models.py | 22 +++++++++++----------- hc/accounts/tests/test_change_email.py | 6 +++--- hc/accounts/tests/test_check_token.py | 2 +- hc/accounts/views.py | 5 ++--- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/hc/accounts/backends.py b/hc/accounts/backends.py index b8c08717..2a3381f5 100644 --- a/hc/accounts/backends.py +++ b/hc/accounts/backends.py @@ -1,4 +1,3 @@ -from django.contrib.auth.hashers import check_password from django.contrib.auth.models import User from hc.accounts.models import Profile @@ -22,7 +21,7 @@ class ProfileBackend(BasicBackend): except Profile.DoesNotExist: return None - if not check_password(token, profile.token): + if not profile.check_token(token, "login"): return None return profile.user diff --git a/hc/accounts/models.py b/hc/accounts/models.py index 2702db78..56810e89 100644 --- a/hc/accounts/models.py +++ b/hc/accounts/models.py @@ -4,7 +4,7 @@ import uuid from datetime import timedelta from django.conf import settings -from django.contrib.auth.hashers import make_password +from django.contrib.auth.hashers import check_password, make_password from django.contrib.auth.models import User from django.core import signing from django.db import models @@ -54,11 +54,17 @@ class Profile(models.Model): def __str__(self): return self.team_name or self.user.email - def send_instant_login_link(self, inviting_profile=None): + def prepare_token(self, salt): token = str(uuid.uuid4()) - self.token = make_password(token) + self.token = make_password(token, salt) self.save() + return token + + def check_token(self, token, salt): + return salt in self.token and check_password(token, self.token) + def send_instant_login_link(self, inviting_profile=None): + token = self.prepare_token("login") path = reverse("hc-check-token", args=[self.user.username, token]) ctx = { "button_text": "Log In", @@ -68,10 +74,7 @@ class Profile(models.Model): emails.login(self.user.email, ctx) def send_set_password_link(self): - token = str(uuid.uuid4()) - self.token = make_password(token) - self.save() - + token = self.prepare_token("set-password") path = reverse("hc-set-password", args=[token]) ctx = { "button_text": "Set Password", @@ -80,10 +83,7 @@ class Profile(models.Model): emails.set_password(self.user.email, ctx) def send_change_email_link(self): - token = str(uuid.uuid4()) - self.token = make_password(token) - self.save() - + token = self.prepare_token("change-email") path = reverse("hc-change-email", args=[token]) ctx = { "button_text": "Change Email", diff --git a/hc/accounts/tests/test_change_email.py b/hc/accounts/tests/test_change_email.py index cdd0a186..523bb868 100644 --- a/hc/accounts/tests/test_change_email.py +++ b/hc/accounts/tests/test_change_email.py @@ -6,7 +6,7 @@ from hc.test import BaseTestCase class ChangeEmailTestCase(BaseTestCase): def test_it_shows_form(self): - self.profile.token = make_password("foo") + self.profile.token = make_password("foo", "change-email") self.profile.save() self.client.login(username="alice@example.org", password="password") @@ -15,7 +15,7 @@ class ChangeEmailTestCase(BaseTestCase): self.assertContains(r, "Change Account's Email Address") def test_it_changes_password(self): - self.profile.token = make_password("foo") + self.profile.token = make_password("foo", "change-email") self.profile.save() self.client.login(username="alice@example.org", password="password") @@ -28,7 +28,7 @@ class ChangeEmailTestCase(BaseTestCase): self.assertFalse(self.alice.has_usable_password()) def test_it_requires_unique_email(self): - self.profile.token = make_password("foo") + self.profile.token = make_password("foo", "change-email") self.profile.save() self.client.login(username="alice@example.org", password="password") diff --git a/hc/accounts/tests/test_check_token.py b/hc/accounts/tests/test_check_token.py index ec778273..b814189c 100644 --- a/hc/accounts/tests/test_check_token.py +++ b/hc/accounts/tests/test_check_token.py @@ -6,7 +6,7 @@ class CheckTokenTestCase(BaseTestCase): def setUp(self): super(CheckTokenTestCase, self).setUp() - self.profile.token = make_password("secret-token") + self.profile.token = make_password("secret-token", "login") self.profile.save() def test_it_shows_form(self): diff --git a/hc/accounts/views.py b/hc/accounts/views.py index 620c40a4..e33b34e2 100644 --- a/hc/accounts/views.py +++ b/hc/accounts/views.py @@ -7,7 +7,6 @@ from django.contrib.auth import login as auth_login from django.contrib.auth import logout as auth_logout from django.contrib.auth import authenticate from django.contrib.auth.decorators import login_required -from django.contrib.auth.hashers import check_password from django.contrib.auth.models import User from django.core import signing from django.http import HttpResponseForbidden, HttpResponseBadRequest @@ -288,7 +287,7 @@ def badges(request): @login_required def set_password(request, token): profile = request.user.profile - if not check_password(token, profile.token): + if not profile.check_token(token, "set-password"): return HttpResponseBadRequest() if request.method == "POST": @@ -315,7 +314,7 @@ def set_password(request, token): @login_required def change_email(request, token): profile = request.user.profile - if not check_password(token, profile.token): + if not profile.check_token(token, "change-email"): return HttpResponseBadRequest() if request.method == "POST":