# HG changeset patch # User Mads Kiilerich # Date 2019-01-04 03:42:17 # Node ID 9c2fc1390291d9bcab701f2f12bd28913f2058f7 # Parent ffdcf0dfe0e4e67e33ae68a16d51a316833fdaff tests: prepare for adding CSRF protection on login forms CSRF is about avoiding abuse of credentials by doing things in existing sessions. The login form does not have any previous credentials, so there is nothing to abuse and no real need for CSRF protection. But there is still an unauth session, so we *can* have CSRF protection. CSRF protection is currently in LoginRequired (which obviously isn't applied to the login form), but let's prepare for changing that. diff --git a/kallithea/tests/base.py b/kallithea/tests/base.py --- a/kallithea/tests/base.py +++ b/kallithea/tests/base.py @@ -154,7 +154,8 @@ class TestController(object): self._logged_username = username response = self.app.post(url(controller='login', action='index'), {'username': username, - 'password': password}) + 'password': password, + '_authentication_token': self.authentication_token()}) if 'Invalid username or password' in response.body: pytest.fail('could not login using %s %s' % (username, password)) diff --git a/kallithea/tests/functional/test_login.py b/kallithea/tests/functional/test_login.py --- a/kallithea/tests/functional/test_login.py +++ b/kallithea/tests/functional/test_login.py @@ -31,7 +31,8 @@ class TestLoginController(TestController def test_login_admin_ok(self): response = self.app.post(url(controller='login', action='index'), {'username': TEST_USER_ADMIN_LOGIN, - 'password': TEST_USER_ADMIN_PASS}) + 'password': TEST_USER_ADMIN_PASS, + '_authentication_token': self.authentication_token()}) assert response.status == '302 Found' self.assert_authenticated_user(response, TEST_USER_ADMIN_LOGIN) @@ -41,7 +42,8 @@ class TestLoginController(TestController def test_login_regular_ok(self): response = self.app.post(url(controller='login', action='index'), {'username': TEST_USER_REGULAR_LOGIN, - 'password': TEST_USER_REGULAR_PASS}) + 'password': TEST_USER_REGULAR_PASS, + '_authentication_token': self.authentication_token()}) assert response.status == '302 Found' self.assert_authenticated_user(response, TEST_USER_REGULAR_LOGIN) @@ -52,7 +54,8 @@ class TestLoginController(TestController def test_login_regular_email_ok(self): response = self.app.post(url(controller='login', action='index'), {'username': TEST_USER_REGULAR_EMAIL, - 'password': TEST_USER_REGULAR_PASS}) + 'password': TEST_USER_REGULAR_PASS, + '_authentication_token': self.authentication_token()}) assert response.status == '302 Found' self.assert_authenticated_user(response, TEST_USER_REGULAR_LOGIN) @@ -65,7 +68,8 @@ class TestLoginController(TestController response = self.app.post(url(controller='login', action='index', came_from=test_came_from), {'username': TEST_USER_ADMIN_LOGIN, - 'password': TEST_USER_ADMIN_PASS}) + 'password': TEST_USER_ADMIN_PASS, + '_authentication_token': self.authentication_token()}) assert response.status == '302 Found' response = response.follow() @@ -76,7 +80,8 @@ class TestLoginController(TestController response = self.app.post(url(controller='login', action='index'), {'username': TEST_USER_REGULAR_LOGIN, 'password': TEST_USER_REGULAR_PASS, - 'remember': False}) + 'remember': False, + '_authentication_token': self.authentication_token()}) assert 'Set-Cookie' in response.headers for cookie in response.headers.getall('Set-Cookie'): @@ -86,7 +91,8 @@ class TestLoginController(TestController response = self.app.post(url(controller='login', action='index'), {'username': TEST_USER_REGULAR_LOGIN, 'password': TEST_USER_REGULAR_PASS, - 'remember': True}) + 'remember': True, + '_authentication_token': self.authentication_token()}) assert 'Set-Cookie' in response.headers for cookie in response.headers.getall('Set-Cookie'): @@ -95,7 +101,8 @@ class TestLoginController(TestController def test_logout(self): response = self.app.post(url(controller='login', action='index'), {'username': TEST_USER_REGULAR_LOGIN, - 'password': TEST_USER_REGULAR_PASS}) + 'password': TEST_USER_REGULAR_PASS, + '_authentication_token': self.authentication_token()}) # Verify that a login session has been established. response = self.app.get(url(controller='login', action='index')) @@ -123,13 +130,15 @@ class TestLoginController(TestController response = self.app.post(url(controller='login', action='index', came_from=url_came_from), {'username': TEST_USER_ADMIN_LOGIN, - 'password': TEST_USER_ADMIN_PASS}, + 'password': TEST_USER_ADMIN_PASS, + '_authentication_token': self.authentication_token()}, status=400) def test_login_short_password(self): response = self.app.post(url(controller='login', action='index'), {'username': TEST_USER_ADMIN_LOGIN, - 'password': 'as'}) + 'password': 'as', + '_authentication_token': self.authentication_token()}) assert response.status == '200 OK' response.mustcontain('Enter 3 characters or more') @@ -137,14 +146,16 @@ class TestLoginController(TestController def test_login_wrong_username_password(self): response = self.app.post(url(controller='login', action='index'), {'username': 'error', - 'password': 'test12'}) + 'password': 'test12', + '_authentication_token': self.authentication_token()}) response.mustcontain('Invalid username or password') def test_login_non_ascii(self): response = self.app.post(url(controller='login', action='index'), {'username': TEST_USER_REGULAR_LOGIN, - 'password': 'blåbærgrød'}) + 'password': 'blåbærgrød', + '_authentication_token': self.authentication_token()}) response.mustcontain('>Invalid username or password<') @@ -187,7 +198,8 @@ class TestLoginController(TestController response = self.app.post(url(controller='login', action='index', came_from=url('/_admin/users', **args)), {'username': TEST_USER_ADMIN_LOGIN, - 'password': TEST_USER_ADMIN_PASS}) + 'password': TEST_USER_ADMIN_PASS, + '_authentication_token': self.authentication_token()}) assert response.status == '302 Found' for encoded in args_encoded: assert encoded in response.location @@ -201,7 +213,8 @@ class TestLoginController(TestController response = self.app.post(url(controller='login', action='index', came_from=url('/_admin/users', **args)), {'username': 'error', - 'password': 'test12'}) + 'password': 'test12', + '_authentication_token': self.authentication_token()}) response.mustcontain('Invalid username or password') came_from = urlparse.parse_qs(urlparse.urlparse(response.form.action).query)['came_from'][0] @@ -223,7 +236,8 @@ class TestLoginController(TestController 'password_confirmation': 'test12', 'email': 'goodmail@example.com', 'firstname': 'test', - 'lastname': 'test'}) + 'lastname': 'test', + '_authentication_token': self.authentication_token()}) with test_context(self.app): msg = validators.ValidUsername()._messages['username_exists'] @@ -237,7 +251,8 @@ class TestLoginController(TestController 'password_confirmation': 'test12', 'email': TEST_USER_ADMIN_EMAIL, 'firstname': 'test', - 'lastname': 'test'}) + 'lastname': 'test', + '_authentication_token': self.authentication_token()}) with test_context(self.app): msg = validators.UniqSystemEmail()()._messages['email_taken'] @@ -250,7 +265,8 @@ class TestLoginController(TestController 'password_confirmation': 'test12', 'email': TEST_USER_ADMIN_EMAIL.title(), 'firstname': 'test', - 'lastname': 'test'}) + 'lastname': 'test', + '_authentication_token': self.authentication_token()}) with test_context(self.app): msg = validators.UniqSystemEmail()()._messages['email_taken'] response.mustcontain(msg) @@ -262,7 +278,8 @@ class TestLoginController(TestController 'password_confirmation': 'test', 'email': 'goodmailm', 'firstname': 'test', - 'lastname': 'test'}) + 'lastname': 'test', + '_authentication_token': self.authentication_token()}) assert response.status == '200 OK' response.mustcontain('An email address must contain a single @') response.mustcontain('Enter a value 6 characters long or more') @@ -274,7 +291,8 @@ class TestLoginController(TestController 'password_confirmation': 'test12', 'email': 'goodmailm', 'firstname': 'test', - 'lastname': 'test'}) + 'lastname': 'test', + '_authentication_token': self.authentication_token()}) response.mustcontain('An email address must contain a single @') response.mustcontain('Username may only contain ' @@ -290,7 +308,8 @@ class TestLoginController(TestController 'password_confirmation': 'test12', 'email': 'goodmailm', 'firstname': 'test', - 'lastname': 'test'}) + 'lastname': 'test', + '_authentication_token': self.authentication_token()}) response.mustcontain('An email address must contain a single @') with test_context(self.app): @@ -305,7 +324,8 @@ class TestLoginController(TestController 'password_confirmation': 'ąćźżąśśśś', 'email': 'goodmailm@test.plx', 'firstname': 'test', - 'lastname': 'test'}) + 'lastname': 'test', + '_authentication_token': self.authentication_token()}) with test_context(self.app): msg = validators.ValidPassword()._messages['invalid_password'] @@ -318,7 +338,8 @@ class TestLoginController(TestController 'password_confirmation': 'qwe123', 'email': 'goodmailm@test.plxa', 'firstname': 'test', - 'lastname': 'test'}) + 'lastname': 'test', + '_authentication_token': self.authentication_token()}) with test_context(self.app): msg = validators.ValidPasswordsMatch('password', 'password_confirmation')._messages['password_mismatch'] response.mustcontain(msg) @@ -337,7 +358,8 @@ class TestLoginController(TestController 'email': email, 'firstname': name, 'lastname': lastname, - 'admin': True}) # This should be overridden + 'admin': True, + '_authentication_token': self.authentication_token()}) # This should be overridden assert response.status == '302 Found' self.checkSessionFlash(response, 'You have successfully registered with Kallithea') @@ -358,8 +380,8 @@ class TestLoginController(TestController bad_email = 'username%wrongmail.org' response = self.app.post( url(controller='login', action='password_reset'), - {'email': bad_email, } - ) + {'email': bad_email, + '_authentication_token': self.authentication_token()}) response.mustcontain('An email address must contain a single @') @@ -387,7 +409,8 @@ class TestLoginController(TestController response = self.app.post(url(controller='login', action='password_reset'), - {'email': email, }) + {'email': email, + '_authentication_token': self.authentication_token()}) self.checkSessionFlash(response, 'A password reset confirmation code has been sent') @@ -404,6 +427,7 @@ class TestLoginController(TestController 'password': "p@ssw0rd", 'password_confirm': "p@ssw0rd", 'token': token, + '_authentication_token': self.authentication_token(), }) assert response.status == '200 OK' response.mustcontain('Invalid password reset token') @@ -431,6 +455,7 @@ class TestLoginController(TestController 'password': "p@ssw0rd", 'password_confirm': "p@ssw0rd", 'token': token, + '_authentication_token': self.authentication_token(), }) assert response.status == '302 Found' self.checkSessionFlash(response, 'Successfully updated password')