def test_reset_password_email_configuration_override(self, body_type): """ Tests that the right url domain and platform name is included in the reset password email """ req = self.request_factory.post('/password_reset/', {'email': self.user.email}) req.get_host = Mock(return_value=None) req.site = Mock(domain='example.com') req.user = self.user with patch('crum.get_current_request', return_value=req): password_reset(req) sent_message = mail.outbox[0] bodies = { 'plain_text': sent_message.body, 'html': sent_message.alternatives[0][0], } body = bodies[body_type] reset_msg = u"you requested a password reset for your user account at {}".format( fake_get_value('PLATFORM_NAME')) self.assertIn(reset_msg, body) self.assert_event_emitted(SETTING_CHANGE_INITIATED, user_id=self.user.id, setting=u'password', old=None, new=None) self.assertEqual(sent_message.from_email, "*****@*****.**")
def test_ratelimited_from_different_ips_with_same_email(self): """ Test that password reset endpoint allow only one request per minute per email address. """ cache.clear() good_req = self.request_factory.post('/password_reset/', {'email': '*****@*****.**'}) good_req.user = AnonymousUser() good_resp = password_reset(good_req) self.assertEqual(good_resp.status_code, 200) # change the IP and verify that the rate limiter should kick in and # give a Forbidden response if the request is for same email address. new_ip = "8.8.8.8" self.assertNotEqual(good_req.META.get('REMOTE_ADDR'), new_ip) bad_req = self.request_factory.post( '/password_reset/', {'email': '*****@*****.**'}, REMOTE_ADDR=new_ip ) bad_req.user = AnonymousUser() bad_resp = password_reset(bad_req) self.assertEqual(bad_resp.status_code, 403) self.assertEqual(bad_req.META.get('REMOTE_ADDR'), new_ip) cache.clear()
def test_reset_password_email_site(self, site_name, platform_name): """ Tests that the right url domain and platform name is included in the reset password email """ with patch("django.conf.settings.PLATFORM_NAME", platform_name): with patch("django.conf.settings.SITE_NAME", site_name): req = self.request_factory.post('/password_reset/', {'email': self.user.email}) req.user = self.user req.site = Mock(domain='example.com') password_reset(req) sent_message = mail.outbox[0] msg = sent_message.body reset_msg = u"you requested a password reset for your user account at {}" reset_msg = reset_msg.format(site_name) self.assertIn(reset_msg, msg) sign_off = u"The {} Team".format(platform_name) self.assertIn(sign_off, msg) self.assert_event_emitted(SETTING_CHANGE_INITIATED, user_id=self.user.id, setting=u'password', old=None, new=None)
def test_reset_password_email_subject(self, platform_name): """ Tests that the right platform name is included in the reset password email subject """ with patch("django.conf.settings.PLATFORM_NAME", platform_name): req = self.request_factory.post('/password_reset/', {'email': self.user.email}) req.user = self.user req.site = Mock(domain='example.com') password_reset(req) sent_message = mail.outbox[0] subj = sent_message.subject self.assertIn(platform_name, subj)
def assert_password_reset_ratelimitted(self, email, user): """ Assert that password reset endpoint allow one request per minute per email. """ cache.clear() password_reset_req = self.request_factory.post('/password_reset/', {'email': email}) password_reset_req.user = user password_reset_req.site = Mock(domain='example.com') good_resp = password_reset(password_reset_req) self.assertEqual(good_resp.status_code, 200) # then the rate limiter should kick in and give a HttpForbidden response bad_resp = password_reset(password_reset_req) self.assertEqual(bad_resp.status_code, 403) cache.clear()
def test_reset_password_email(self, body_type, expected_output): """Tests contents of reset password email, and that user is not active""" good_req = self.request_factory.post('/password_reset/', {'email': self.user.email}) good_req.user = self.user good_req.site = Mock(domain='example.com') dot_application = dot_factories.ApplicationFactory(user=self.user) dot_access_token = dot_factories.AccessTokenFactory( user=self.user, application=dot_application) dot_factories.RefreshTokenFactory(user=self.user, application=dot_application, access_token=dot_access_token) good_resp = password_reset(good_req) assert good_resp.status_code == 200 assert not dot_models.AccessToken.objects.filter( user=self.user).exists() assert not dot_models.RefreshToken.objects.filter( user=self.user).exists() obj = json.loads(good_resp.content.decode('utf-8')) assert obj['success'] assert 'e-mailed you instructions for setting your password' in obj[ 'value'] from_email = configuration_helpers.get_value( 'email_from_address', settings.DEFAULT_FROM_EMAIL) sent_message = mail.outbox[0] bodies = { 'plain_text': sent_message.body, 'html': sent_message.alternatives[0][0], } body = bodies[body_type] if django.VERSION >= (3, 0) and body_type == 'html': expected_output = "You're receiving this e-mail because you requested a password reset" assert 'Password reset' in sent_message.subject assert expected_output in body assert sent_message.from_email == from_email assert len(sent_message.to) == 1 assert self.user.email in sent_message.to self.assert_event_emitted( SETTING_CHANGE_INITIATED, user_id=self.user.id, setting='password', old=None, new=None, ) # Test that the user is not active self.user = User.objects.get(pk=self.user.pk) assert not self.user.is_active assert 'password_reset_confirm/' in body re.search( r'password_reset_confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/', body).groupdict()
def test_ratelimitted_from_same_ip_with_different_email(self): """ Test that password reset endpoint allow only one request per minute per IP. """ cache.clear() good_req = self.request_factory.post('/password_reset/', {'email': '*****@*****.**'}) good_req.user = AnonymousUser() good_resp = password_reset(good_req) self.assertEqual(good_resp.status_code, 200) # change the email ID and verify that the rate limiter should kick in and # give a Forbidden response if the request is from same IP. bad_req = self.request_factory.post('/password_reset/', {'email': '*****@*****.**'}) bad_req.user = AnonymousUser() bad_resp = password_reset(bad_req) self.assertEqual(bad_resp.status_code, 403) cache.clear()
def test_password_reset_ratelimited(self): """ Test that reset password endpoint only allow one request per minute. """ cache.clear() password_reset_req = self.request_factory.post( '/password_reset/', {'email': '*****@*****.**'}) password_reset_req.user = AnonymousUser() good_resp = password_reset(password_reset_req) self.assertEqual(good_resp.status_code, 200) # then the rate limiter should kick in and give a HttpForbidden response bad_resp = password_reset(password_reset_req) self.assertEqual(bad_resp.status_code, 403) self.assert_no_events_were_emitted() cache.clear()
def test_reset_password_email(self, body_type, expected_output): """Tests contents of reset password email, and that user is not active""" good_req = self.request_factory.post('/password_reset/', {'email': self.user.email}) good_req.user = self.user good_req.site = Mock(domain='example.com') dot_application = dot_factories.ApplicationFactory(user=self.user) dot_access_token = dot_factories.AccessTokenFactory( user=self.user, application=dot_application) dot_factories.RefreshTokenFactory(user=self.user, application=dot_application, access_token=dot_access_token) good_resp = password_reset(good_req) self.assertEqual(good_resp.status_code, 200) self.assertFalse( dot_models.AccessToken.objects.filter(user=self.user).exists()) self.assertFalse( dot_models.RefreshToken.objects.filter(user=self.user).exists()) obj = json.loads(good_resp.content.decode('utf-8')) self.assertTrue(obj['success']) self.assertIn('e-mailed you instructions for setting your password', obj['value']) from_email = configuration_helpers.get_value( 'email_from_address', settings.DEFAULT_FROM_EMAIL) sent_message = mail.outbox[0] bodies = { 'plain_text': sent_message.body, 'html': sent_message.alternatives[0][0], } body = bodies[body_type] self.assertIn("Password reset", sent_message.subject) self.assertIn(expected_output, body) self.assertEqual(sent_message.from_email, from_email) self.assertEqual(len(sent_message.to), 1) self.assertIn(self.user.email, sent_message.to) self.assert_event_emitted( SETTING_CHANGE_INITIATED, user_id=self.user.id, setting=u'password', old=None, new=None, ) # Test that the user is not active self.user = User.objects.get(pk=self.user.pk) self.assertFalse(self.user.is_active) self.assertIn('password_reset_confirm/', body) re.search( r'password_reset_confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/', body).groupdict()
def test_reset_password_email_https(self, is_secure, protocol, send_email): """ Tests that the right url protocol is included in the reset password link """ req = self.request_factory.post( '/password_reset/', {'email': self.user.email} ) req.site = Mock(domain='example.com') req.is_secure = Mock(return_value=is_secure) req.user = self.user password_reset(req) _, msg, _, _ = send_email.call_args[0] expected_msg = "Please go to the following page and choose a new password:\n\n" + protocol self.assertIn(expected_msg, msg) self.assert_event_emitted( SETTING_CHANGE_INITIATED, user_id=self.user.id, setting=u'password', old=None, new=None )
def test_password_reset_ratelimited(self): """ Try (and fail) resetting password 30 times in a row on an non-existant email address """ cache.clear() for i in range(30): good_req = self.request_factory.post('/password_reset/', { 'email': 'thisdoesnotexist{0}@foo.com'.format(i) }) good_resp = password_reset(good_req) self.assertEquals(good_resp.status_code, 200) # then the rate limiter should kick in and give a HttpForbidden response bad_req = self.request_factory.post('/password_reset/', {'email': '*****@*****.**'}) bad_resp = password_reset(bad_req) self.assertEquals(bad_resp.status_code, 403) self.assert_no_events_were_emitted() cache.clear()
def request_password_reset(self, status, new_ip=None): extra_args = {} if new_ip: extra_args = {'REMOTE_ADDR': new_ip} reset_request = self.request_factory.post( '/password_reset/', {'email': '*****@*****.**'}, **extra_args) if new_ip: self.assertEqual(reset_request.META.get('REMOTE_ADDR'), new_ip) reset_request.user = AnonymousUser() response = password_reset(reset_request) self.assertEqual(response.status_code, status)
def test_user_bad_password_reset(self): """ Tests password reset behavior for user with password marked UNUSABLE_PASSWORD_PREFIX """ bad_pwd_req = self.request_factory.post('/password_reset/', {'email': self.user_bad_passwd.email}) bad_pwd_resp = password_reset(bad_pwd_req) # If they've got an unusable password, we return a successful response code self.assertEquals(bad_pwd_resp.status_code, 200) obj = json.loads(bad_pwd_resp.content.decode('utf-8')) self.assertEquals(obj, { 'success': True, 'value': "('registration/password_reset_done.html', [])", }) self.assert_no_events_were_emitted()
def request_password_reset(self, status, new_ip=None): # lint-amnesty, pylint: disable=missing-function-docstring extra_args = {} if new_ip: extra_args = {'REMOTE_ADDR': new_ip} reset_request = self.request_factory.post( '/password_reset/', {'email': '*****@*****.**'}, **extra_args) if new_ip: assert reset_request.META.get('REMOTE_ADDR') == new_ip reset_request.user = AnonymousUser() response = password_reset(reset_request) assert response.status_code == status
def test_nonexist_email_password_reset(self): """ Now test the exception cases with of reset_password called with invalid email. """ bad_email_req = self.request_factory.post('/password_reset/', {'email': self.user.email + "makeItFail"}) bad_email_resp = password_reset(bad_email_req) # Note: even if the email is bad, we return a successful response code # This prevents someone potentially trying to "brute-force" find out which # emails are and aren't registered with edX self.assertEquals(bad_email_resp.status_code, 200) obj = json.loads(bad_email_resp.content.decode('utf-8')) self.assertEquals(obj, { 'success': True, 'value': "('registration/password_reset_done.html', [])", }) self.assert_no_events_were_emitted()