From 8a3d7a57aca13e614ccbf744e4226c139825768b Mon Sep 17 00:00:00 2001 From: Ken Cochrane Date: Wed, 31 Dec 2014 20:29:17 -0500 Subject: [PATCH] more refactor and unit tests --- defender/tests.py | 111 +++++++++++++++++++++++++++++++++------------- 1 file changed, 80 insertions(+), 31 deletions(-) diff --git a/defender/tests.py b/defender/tests.py index 2e3a5c6..3c83e2b 100644 --- a/defender/tests.py +++ b/defender/tests.py @@ -9,11 +9,10 @@ from django.test import TestCase from django.contrib.auth.models import User from django.core.urlresolvers import NoReverseMatch from django.core.urlresolvers import reverse +from django.test.utils import override_settings +from django.conf import settings -from .utils import ( - COOLOFF_TIME, FAILURE_LIMIT, reset_failed_attempts, - is_valid_ip) - +from . import utils redis_client = mockredis.mock_strict_redis_client() @@ -27,7 +26,7 @@ except NoReverseMatch: class AccessAttemptTest(TestCase): - """Test case using custom settings for testing + """ Test case using custom settings for testing """ VALID_USERNAME = 'valid' LOCKED_MESSAGE = 'Account locked: too many login attempts.' @@ -40,7 +39,7 @@ class AccessAttemptTest(TestCase): @patch('defender.utils.redis_server', redis_client) def _login(self, is_valid=False, user_agent='test-browser'): - """Login a user. A valid credential is used when is_valid is True, + """ Login a user. A valid credential is used when is_valid is True, otherwise it will use a random string to make a failed login. """ username = self.VALID_USERNAME if is_valid else self._get_random_str() @@ -55,7 +54,7 @@ class AccessAttemptTest(TestCase): @patch('defender.utils.redis_server', redis_client) def setUp(self): - """Create a valid user for login + """ Create a valid user for login """ self.user = User.objects.create_superuser( username=self.VALID_USERNAME, @@ -69,10 +68,10 @@ class AccessAttemptTest(TestCase): @patch('defender.utils.redis_server', redis_client) def test_failure_limit_once(self): - """Tests the login lock trying to login one more time + """ Tests the login lock trying to login one more time than failure limit """ - for i in range(0, FAILURE_LIMIT): + for i in range(0, utils.FAILURE_LIMIT): response = self._login() # Check if we are in the same login page self.assertContains(response, LOGIN_FORM_KEY) @@ -82,12 +81,16 @@ class AccessAttemptTest(TestCase): response = self._login() self.assertContains(response, self.LOCKED_MESSAGE) + # doing a get should also get locked out message + response = self.client.get(ADMIN_LOGIN_URL) + self.assertContains(response, self.LOCKED_MESSAGE) + @patch('defender.utils.redis_server', redis_client) def test_failure_limit_many(self): - """Tests the login lock trying to login a lot of times more + """ Tests the login lock trying to login a lot of times more than failure limit """ - for i in range(0, FAILURE_LIMIT): + for i in range(0, utils.FAILURE_LIMIT): response = self._login() # Check if we are in the same login page self.assertContains(response, LOGIN_FORM_KEY) @@ -99,20 +102,24 @@ class AccessAttemptTest(TestCase): response = self._login() self.assertContains(response, self.LOCKED_MESSAGE) + # doing a get should also get locked out message + response = self.client.get(ADMIN_LOGIN_URL) + self.assertContains(response, self.LOCKED_MESSAGE) + @patch('defender.utils.redis_server', redis_client) def test_valid_login(self): - """Tests a valid login for a real username + """ Tests a valid login for a real username """ response = self._login(is_valid=True) self.assertNotContains(response, LOGIN_FORM_KEY, status_code=302) @patch('defender.utils.redis_server', redis_client) def test_cooling_off(self): - """Tests if the cooling time allows a user to login + """ Tests if the cooling time allows a user to login """ self.test_failure_limit_once() # Wait for the cooling off period - time.sleep(COOLOFF_TIME) + time.sleep(utils.COOLOFF_TIME) # mock redis require that we expire on our own redis_client.do_expire() # It should be possible to login again, make sure it is. @@ -120,14 +127,14 @@ class AccessAttemptTest(TestCase): @patch('defender.utils.redis_server', redis_client) def test_cooling_off_for_trusted_user(self): - """Test the cooling time for a trusted user + """ Test the cooling time for a trusted user """ # Try the cooling off time self.test_cooling_off() @patch('defender.utils.redis_server', redis_client) def test_long_user_agent_valid(self): - """Tests if can handle a long user agent + """ Tests if can handle a long user agent """ long_user_agent = 'ie6' * 1024 response = self._login(is_valid=True, user_agent=long_user_agent) @@ -135,36 +142,78 @@ class AccessAttemptTest(TestCase): @patch('defender.utils.redis_server', redis_client) def test_long_user_agent_not_valid(self): - """Tests if can handle a long user agent with failure + """ Tests if can handle a long user agent with failure """ long_user_agent = 'ie6' * 1024 - for i in range(0, FAILURE_LIMIT + 1): + for i in range(0, utils.FAILURE_LIMIT + 1): response = self._login(user_agent=long_user_agent) self.assertContains(response, self.LOCKED_MESSAGE) @patch('defender.utils.redis_server', redis_client) def test_reset_ip(self): - """Tests if can reset an ip address + """ Tests if can reset an ip address """ # Make a lockout self.test_failure_limit_once() # Reset the ip so we can try again - reset_failed_attempts(ip='127.0.0.1') + utils.reset_failed_attempts(ip='127.0.0.1') # Make a login attempt again self.test_valid_login() - def test_is_valid_ip(self): - """ - Test the is_valid_ip() method - """ + @patch('defender.utils.LOCKOUT_URL', 'http://localhost/othe/login/') + @patch('defender.utils.redis_server', redis_client) + def test_failed_login_redirect_to_URL(self): + """ Test to make sure that after lockout we send to the correct + redirect URL """ - self.assertEquals(is_valid_ip('192.168.0.1'), True) - self.assertEquals(is_valid_ip('130.80.100.24'), True) - self.assertEquals(is_valid_ip('8.8.8.8'), True) - self.assertEquals(is_valid_ip('127.0.0.1'), True) - self.assertEquals(is_valid_ip('fish'), False) - self.assertEquals(is_valid_ip(None), False) - self.assertEquals(is_valid_ip(''), False) + for i in range(0, utils.FAILURE_LIMIT): + response = self._login() + # Check if we are in the same login page + self.assertContains(response, LOGIN_FORM_KEY) + + # So, we shouldn't have gotten a lock-out yet. + # But we should get one now, check redirect make sure it is valid. + response = self._login() + self.assertEquals(response.status_code, 302) + self.assertEquals(response['Location'], 'http://localhost/othe/login/') + + # doing a get should also get locked out message + response = self.client.get(ADMIN_LOGIN_URL) + self.assertEquals(response.status_code, 302) + self.assertEquals(response['Location'], 'http://localhost/othe/login/') + + @patch('defender.utils.LOCKOUT_URL', '/o/login/') + @patch('defender.utils.redis_server', redis_client) + def test_failed_login_redirect_to_URL_local(self): + """ Test to make sure that after lockout we send to the correct + redirect URL """ + + for i in range(0, utils.FAILURE_LIMIT): + response = self._login() + # Check if we are in the same login page + self.assertContains(response, LOGIN_FORM_KEY) + + # So, we shouldn't have gotten a lock-out yet. + # But we should get one now, check redirect make sure it is valid. + response = self._login() + self.assertEquals(response.status_code, 302) + self.assertEquals(response['Location'], 'http://testserver/o/login/') + + # doing a get should also get locked out message + response = self.client.get(ADMIN_LOGIN_URL) + self.assertEquals(response.status_code, 302) + self.assertEquals(response['Location'], 'http://testserver/o/login/') + + def test_is_valid_ip(self): + """ Test the is_valid_ip() method + """ + self.assertEquals(utils.is_valid_ip('192.168.0.1'), True) + self.assertEquals(utils.is_valid_ip('130.80.100.24'), True) + self.assertEquals(utils.is_valid_ip('8.8.8.8'), True) + self.assertEquals(utils.is_valid_ip('127.0.0.1'), True) + self.assertEquals(utils.is_valid_ip('fish'), False) + self.assertEquals(utils.is_valid_ip(None), False) + self.assertEquals(utils.is_valid_ip(''), False)