diff --git a/teraserver/python/modules/FlaskModule/API/user/UserLoginChangePassword.py b/teraserver/python/modules/FlaskModule/API/user/UserLoginChangePassword.py index 78eb2463..c4b58e87 100644 --- a/teraserver/python/modules/FlaskModule/API/user/UserLoginChangePassword.py +++ b/teraserver/python/modules/FlaskModule/API/user/UserLoginChangePassword.py @@ -33,6 +33,9 @@ def post(self): if new_password != confirm_password: return gettext('New password and confirm password do not match'), 400 + if not current_user.user_force_password_change: + return gettext('User not required to change password'), 400 + # Change password, will be encrypted # Will also reset force password change flag try: @@ -43,7 +46,7 @@ def post(self): except UserNewPasswordSameAsOld: return gettext('New password same as old password'), 400 - return redirect(self._generate_login_url()) + return 200 except Exception as e: # Something went wrong, logout user self._user_logout() diff --git a/teraserver/python/templates/login_change_password.html b/teraserver/python/templates/login_change_password.html index 15fc1daf..787db778 100644 --- a/teraserver/python/templates/login_change_password.html +++ b/teraserver/python/templates/login_change_password.html @@ -68,7 +68,7 @@ // Send the form data to the backend with a post request $.ajax({ type: "POST", - url: $(this).action, + url: 'api/user/login/change_password', data: form.serialize(), success: function(response) { $('#dlgRedirect').removeClass("d-none").addClass("d-flex"); @@ -77,7 +77,8 @@ error: function(response) { if (response.status === 401) redirectToLogin(); - $('#error_message')[0].innerHTML = response.responseText; + $('#error_message')[0].innerHTML = response.responseText.substring(1, response.responseText.length-2); + //$('#error_message').text(response.responseJSON); $('#error_message').show(); } }); @@ -104,7 +105,7 @@
5:00
-
+
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogin2FA.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogin2FA.py index 16f5ef6a..0f9a1a13 100644 --- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogin2FA.py +++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogin2FA.py @@ -20,7 +20,6 @@ def tearDown(self): TeraUser.delete(self.user2['id_user'], hard_delete=True) super().tearDown() - def _create_2fa_enabled_user(self, username, password, set_secret:bool = True): user = TeraUser() user.id_user = 0 # New user @@ -41,7 +40,6 @@ def _create_2fa_enabled_user(self, username, password, set_secret:bool = True): TeraUser.insert(user) return user.to_json(minimal=False) - def _login_user(self, username, password): response = self._get_with_user_http_auth(self.test_client, username, password, endpoint='/api/user/login') self.assertEqual(200, response.status_code) diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLoginChangePassword.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLoginChangePassword.py new file mode 100644 index 00000000..6346a86d --- /dev/null +++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLoginChangePassword.py @@ -0,0 +1,179 @@ +from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest +from opentera.db.models.TeraUser import TeraUser + + +class UserLoginChangePassword(BaseUserAPITest): + test_endpoint = '/api/user/login/change_password' + + def setUp(self): + super().setUp() + # Create users with password change needed + with self._flask_app.app_context(): + self.user1: dict = self._create_change_password_user('test_user_1', 'Password12345!') + + def tearDown(self): + # Delete users with 2fa enabled + with self._flask_app.app_context(): + TeraUser.delete(self.user1['id_user'], hard_delete=True) + super().tearDown() + + @staticmethod + def _create_change_password_user(username, password): + user = TeraUser() + user.id_user = 0 # New user + user.user_username = username + user.user_password = password + user.user_firstname = username + user.user_lastname = username + user.user_email = f"{username}@test.com" + user.user_enabled = True + user.user_profile = {} + user.user_force_password_change = True + + TeraUser.insert(user) + return user.to_json(minimal=False) + + def _login_user(self, username, password): + response = self._get_with_user_http_auth(self.test_client, username, password, endpoint='/api/user/login') + self.assertEqual(200, response.status_code) + self.assertEqual('application/json', response.headers['Content-Type']) + self.assertGreater(len(response.json), 0) + return response + + def test_post_endpoint_no_auth(self): + with self._flask_app.app_context(): + response = self.test_client.post(self.test_endpoint) + self.assertEqual(401, response.status_code) + + def test_post_endpoint_invalid_token_auth(self): + with self._flask_app.app_context(): + response = self._post_with_user_token_auth(self.test_client, 'invalid') + self.assertEqual(401, response.status_code) + + def test_post_endpoint_with_no_session(self): + with self._flask_app.app_context(): + response = self.test_client.post(self.test_endpoint) + self.assertEqual(401, response.status_code) + + def test_get_endpoint(self): + with self._flask_app.app_context(): + response = self.test_client.get(self.test_endpoint) + self.assertEqual(405, response.status_code) + + def test_post_password_without_force_required(self): + with self._flask_app.app_context(): + # First login to create session + response = self._login_user('admin', 'admin') + self.assertEqual(200, response.status_code) + self.assertFalse('redirect_url' in response.json) + + def test_post_password_change_mismatched(self): + with self._flask_app.app_context(): + # First login to create session + response = self._login_user('test_user_1', 'Password12345!') + self.assertEqual(200, response.status_code) + self.assertTrue('redirect_url' in response.json) + self.assertTrue('login_change_password' in response.json['redirect_url']) + + params = {'new_password': 'NewPassword12345!', + 'confirm_password': 'NotNewPassword12345!'} + response = self.test_client.post(self.test_endpoint, json=params) + self.assertEqual(400, response.status_code) + + def test_post_password_change_same_as_old(self): + with self._flask_app.app_context(): + # First login to create session + response = self._login_user('test_user_1', 'Password12345!') + self.assertEqual(200, response.status_code) + self.assertTrue('redirect_url' in response.json) + self.assertTrue('login_change_password' in response.json['redirect_url']) + + params = {'new_password': 'Password12345!', + 'confirm_password': 'Password12345!'} + response = self.test_client.post(self.test_endpoint, json=params) + self.assertEqual(400, response.status_code) + + def test_post_password_change_insecure(self): + with self._flask_app.app_context(): + # First login to create session + response = self._login_user('test_user_1', 'Password12345!') + self.assertEqual(200, response.status_code) + self.assertTrue('redirect_url' in response.json) + self.assertTrue('login_change_password' in response.json['redirect_url']) + + json_data = {'new_password': 'password', 'confirm_password': 'password'} + response = self.test_client.post(self.test_endpoint, json=json_data) + self.assertEqual(400, response.status_code, msg="Password not long enough") + + json_data['new_password'] = 'password12345!' + json_data['confirm_password'] = json_data['new_password'] + response = self.test_client.post(self.test_endpoint, json=json_data) + self.assertEqual(400, response.status_code, msg="Password without capital letters") + + json_data['new_password'] = 'PASSWORD12345!' + json_data['confirm_password'] = json_data['new_password'] + response = self.test_client.post(self.test_endpoint, json=json_data) + self.assertEqual(400, response.status_code, msg="Password without lower case letters") + + json_data['new_password'] = 'Password12345' + json_data['confirm_password'] = json_data['new_password'] + response = self.test_client.post(self.test_endpoint, json=json_data) + self.assertEqual(400, response.status_code, msg="Password without special characters") + + json_data['new_password'] = 'Password!!!!' + json_data['confirm_password'] = json_data['new_password'] + response = self.test_client.post(self.test_endpoint, json=json_data) + self.assertEqual(400, response.status_code, msg="Password without numbers") + + json_data['new_password'] = 'Password12345!!' + json_data['confirm_password'] = json_data['new_password'] + response = self.test_client.post(self.test_endpoint, json=json_data) + self.assertEqual(200, response.status_code, msg="Password OK") + + # Reset to original password + user = TeraUser.get_user_by_id(self.user1['id_user']) + user.user_force_password_change = True + user.db().session.commit() + + json_data['new_password'] = 'Password12345!' + json_data['confirm_password'] = json_data['new_password'] + response = self.test_client.post(self.test_endpoint, json=json_data) + self.assertEqual(200, response.status_code, msg="Password back to last") + + def test_post_password_change_not_required(self): + with self._flask_app.app_context(): + # First login to create session + response = self._login_user('test_user_1', 'Password12345!') + self.assertEqual(200, response.status_code) + self.assertTrue('redirect_url' in response.json) + self.assertTrue('login_change_password' in response.json['redirect_url']) + + user = TeraUser.get_user_by_id(self.user1['id_user']) + user.user_force_password_change = False + user.db().session.commit() + + json_data = {'new_password': 'Password12345!!', 'confirm_password': 'Password12345!!'} + response = self.test_client.post(self.test_endpoint, json=json_data) + self.assertEqual(400, response.status_code, msg="Password not required to be changed") + + user = TeraUser.get_user_by_id(self.user1['id_user']) + user.user_force_password_change = True + user.db().session.commit() + + def test_post_password_change_ok(self): + with self._flask_app.app_context(): + # First login to create session + response = self._login_user('test_user_1', 'Password12345!') + self.assertEqual(200, response.status_code) + self.assertTrue('redirect_url' in response.json) + self.assertTrue('login_change_password' in response.json['redirect_url']) + + json_data = {'new_password': 'Password12345!!', 'confirm_password': 'Password12345!!'} + response = self.test_client.post(self.test_endpoint, json=json_data) + self.assertEqual(200, response.status_code, msg="Password OK") + + # Reset to original password + user = TeraUser.get_user_by_id(self.user1['id_user']) + user.user_force_password_change = True + user.user_password = 'Password12345!' + user.db().session.commit() \ No newline at end of file diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLoginSetup2FA.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLoginSetup2FA.py index 4c18b9e1..21fb8873 100644 --- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLoginSetup2FA.py +++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLoginSetup2FA.py @@ -78,7 +78,7 @@ def test_get_endpoint_with_admin_without_2fa_enabled(self): def test_get_endpoint_login_user1_2fa_already_setup(self): with self._flask_app.app_context(): - # Fisrt login to create session + # First login to create session response = self._login_user('test_user_2fa_1', 'Password12345!') self.assertEqual(200, response.status_code) self.assertTrue('redirect_url' in response.json)