Add password reset tests

This commit is contained in:
Christian Cueni 2019-10-02 13:06:16 +02:00
parent 46518a22f8
commit 7e92753977
2 changed files with 162 additions and 6 deletions

View File

@ -7,6 +7,8 @@
#
# Created on 2019-10-01
# @author: chrigu <christian.cueni@iterativ.ch>
import re
import graphene
from django.contrib.auth import authenticate, login
from django.contrib.auth.forms import PasswordResetForm
@ -60,10 +62,14 @@ class PasswordReset(relay.ClientIDMutation):
except User.DoesNotExist:
return cls(success=False, errors=['invalid_email'])
password_reset_view = PasswordResetView()
password_reset_view.request = info.context
form = password_reset_view.form_class({'email': user.email})
if not form.is_valid():
return cls(success=False, errors=form.errors)
try:
password_reset_view = PasswordResetView(request=info.context)
form = password_reset_view.form_class({'email': user.email})
form.is_valid()
password_reset_view.form_valid(form)
except Exception:
return cls(success=False, errors=['email_error'])
@ -119,9 +125,12 @@ class PasswordResetSetPassword(relay.ClientIDMutation, PasswordConfirm):
# fake ordinary POST for view
info.context.POST = {
'new_password1': new_password,
'new_password2': confirm_new_password_input
}
'new_password1': new_password,
'new_password2': confirm_new_password_input
}
if not cls.verify_strong_password(new_password):
return cls(success=False, errors=['invalid_passwords'])
http_response = cls.verify_token_and_uidb64(INTERNAL_RESET_URL_TOKEN, uidb64, info.context)
@ -130,6 +139,17 @@ class PasswordResetSetPassword(relay.ClientIDMutation, PasswordConfirm):
else:
return cls(success=False, errors=['invalid_passwords']) # todo: check error from form
@classmethod
def verify_strong_password(cls, password):
if len(password) == 0:
return password
has_number = re.search('\d', password)
has_upper = re.search('[A-Z]', password)
has_lower = re.search('[a-z]', password)
has_special = re.search('[!@#$%^&*(),.?":{}|<>\+]', password)
return has_number and has_upper and has_lower and has_special
class UserMutations:
login = Login.Field()

View File

@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
#
# ITerativ GmbH
# http://www.iterativ.ch/
#
# Copyright (c) 2019 ITerativ GmbH. All rights reserved.
#
# Created on 2019-10-02
# @author: chrigu <christian.cueni@iterativ.ch>
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.contrib.sessions.middleware import SessionMiddleware
from django.core import mail
from django.test import TestCase, RequestFactory
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from graphene.test import Client
from api.schema_public import schema
from core.factories import UserFactory
class PasswordResetTests(TestCase):
def setUp(self):
self.user = UserFactory(username='aschi')
request = RequestFactory().post('/')
# adding session
middleware = SessionMiddleware()
middleware.process_request(request)
request.session.save()
self.client = Client(schema=schema, context_value=request)
def make_reset_mutation(self, email):
mutation = '''
mutation PasswordReset($input: PasswordResetInput!){
passwordReset(input: $input) {
success
errors {
field
}
}
}
'''
return self.client.execute(mutation, variables={
'input': {
'emailInput': email
}
})
def make_set_verify_mutation(self, uidb64, token):
mutation = '''
mutation PasswordResetVerify($input: PasswordResetVerifyInput!){
passwordResetVerify(input: $input) {
success
errors {
field
}
}
}
'''
return self.client.execute(mutation, variables={
'input': {
'uidb64Input': uidb64,
'tokenInput': token
}
})
def make_set_password_mutation(self, uidb64, new_password, new_password_confirm):
mutation = '''
mutation PasswordResetSetPassword($input: PasswordResetSetPasswordInput!){
passwordResetSetPassword(input: $input) {
success
errors {
field
}
}
}
'''
return self.client.execute(mutation, variables={
'input': {
'uidb64Input': uidb64,
'newPasswordInput': new_password,
'confirmNewPasswordInput': new_password_confirm,
}
})
def test_user_can_initiate_password(self):
result = self.make_reset_mutation(self.user.email)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject.startswith('Passwort auf'), True)
self.assertEqual(result.get('data').get('passwordReset').get('success'), True)
def test_user_can_verify_and_set_password(self):
token_generator = PasswordResetTokenGenerator()
token = token_generator.make_token(self.user)
uidb64 = urlsafe_base64_encode(force_bytes(self.user.pk)).decode()
result = self.make_set_verify_mutation(uidb64, token)
self.assertEqual(result.get('data').get('passwordResetVerify').get('success'), True)
new_password = 'Abcd1234!'
set_result = self.make_set_password_mutation(uidb64, new_password, new_password)
print(set_result)
self.assertEqual(set_result.get('data').get('passwordResetSetPassword').get('success'), True)
def test_user_cannot_use_unsafe_password(self):
token_generator = PasswordResetTokenGenerator()
token = token_generator.make_token(self.user)
uidb64 = urlsafe_base64_encode(force_bytes(self.user.pk)).decode()
result = self.make_set_verify_mutation(uidb64, token)
self.assertEqual(result.get('data').get('passwordResetVerify').get('success'), True)
new_password = 'test'
set_result = self.make_set_password_mutation(uidb64, new_password, new_password)
self.assertEqual(set_result.get('data').get('passwordResetSetPassword').get('success'), False)
def test_new_passwords_must_match(self):
token_generator = PasswordResetTokenGenerator()
token = token_generator.make_token(self.user)
uidb64 = urlsafe_base64_encode(force_bytes(self.user.pk)).decode()
result = self.make_set_verify_mutation(uidb64, token)
self.assertEqual(result.get('data').get('passwordResetVerify').get('success'), True)
new_password = 'Abcd1234!'
new_password_confirm = 'Abcd1234!1'
set_result = self.make_set_password_mutation(uidb64, new_password, new_password_confirm)
self.assertEqual(set_result.get('data').get('passwordResetSetPassword').get('success'), False)