diff --git a/fittrackee/tests/users/test_auth_api.py b/fittrackee/tests/users/test_auth_api.py index 53158fb8..bdb92f6b 100644 --- a/fittrackee/tests/users/test_auth_api.py +++ b/fittrackee/tests/users/test_auth_api.py @@ -593,25 +593,9 @@ class TestUserAccountUpdate(ApiTestCaseMixin): headers=dict(Authorization=f'Bearer {auth_token}'), ) - self.assert_400(response) + self.assert_400(response, error_message='current password is missing') - def test_it_updates_user_profile(self, app: Flask, user_1: User) -> None: - client, auth_token = self.get_test_client_and_auth_token( - app, user_1.email - ) - - response = client.patch( - '/api/auth/profile/edit/account', - content_type='application/json', - data=json.dumps(dict(password=random_string())), - headers=dict(Authorization=f'Bearer {auth_token}'), - ) - assert response.status_code == 200 - data = json.loads(response.data.decode()) - assert data['status'] == 'success' - assert data['message'] == 'user account updated' - - def test_it_returns_error_if_controls_fail( + def test_it_returns_error_if_current_password_is_missing( self, app: Flask, user_1: User ) -> None: client, auth_token = self.get_test_client_and_auth_token( @@ -621,14 +605,99 @@ class TestUserAccountUpdate(ApiTestCaseMixin): response = client.patch( '/api/auth/profile/edit/account', content_type='application/json', - data=json.dumps(dict(password=random_string(length=5))), + data=json.dumps(dict(new_password=random_string())), headers=dict(Authorization=f'Bearer {auth_token}'), ) - self.assert_400( - response, error_message='password: 8 characters required' + self.assert_400(response, error_message='current password is missing') + + def test_it_returns_error_if_current_password_is_invalid( + self, app: Flask, user_1: User + ) -> None: + client, auth_token = self.get_test_client_and_auth_token( + app, user_1.email ) + response = client.patch( + '/api/auth/profile/edit/account', + content_type='application/json', + data=json.dumps( + dict( + password=random_string(), + new_password=random_string(), + ) + ), + headers=dict(Authorization=f'Bearer {auth_token}'), + ) + + self.assert_401(response, error_message='invalid credentials') + + def test_it_returns_error_if_controls_fail_on_new_password( + self, app: Flask, user_1: User + ) -> None: + client, auth_token = self.get_test_client_and_auth_token( + app, user_1.email + ) + + response = client.patch( + '/api/auth/profile/edit/account', + content_type='application/json', + data=json.dumps( + dict( + password='12345678', + new_password=random_string(length=3), + ) + ), + headers=dict(Authorization=f'Bearer {auth_token}'), + ) + + self.assert_400(response, 'password: 8 characters required') + + def test_it_updates_auth_user_password( + self, app: Flask, user_1: User + ) -> None: + client, auth_token = self.get_test_client_and_auth_token( + app, user_1.email + ) + current_hashed_password = user_1.password + + response = client.patch( + '/api/auth/profile/edit/account', + content_type='application/json', + data=json.dumps( + dict( + password='12345678', + new_password=random_string(), + ) + ), + headers=dict(Authorization=f'Bearer {auth_token}'), + ) + assert response.status_code == 200 + data = json.loads(response.data.decode()) + assert data['status'] == 'success' + assert data['message'] == 'user account updated' + assert current_hashed_password != user_1.password + + def test_new_password_is_hashed(self, app: Flask, user_1: User) -> None: + client, auth_token = self.get_test_client_and_auth_token( + app, user_1.email + ) + new_password = random_string() + + response = client.patch( + '/api/auth/profile/edit/account', + content_type='application/json', + data=json.dumps( + dict( + password='12345678', + new_password=new_password, + ) + ), + headers=dict(Authorization=f'Bearer {auth_token}'), + ) + assert response.status_code == 200 + assert new_password != user_1.password + class TestUserPreferencesUpdate(ApiTestCaseMixin): def test_it_updates_user_preferences( diff --git a/fittrackee/users/auth.py b/fittrackee/users/auth.py index 3106ddcf..a2f94867 100644 --- a/fittrackee/users/auth.py +++ b/fittrackee/users/auth.py @@ -626,7 +626,7 @@ def update_user_account(auth_user: User) -> Union[Dict, HttpResponse]: "username": "sam" "weekm": true, }, - "message": "user profile updated", + "message": "user account updated", "status": "success" } @@ -646,19 +646,23 @@ def update_user_account(auth_user: User) -> Union[Dict, HttpResponse]: """ data = request.get_json() - if not data: - return InvalidPayloadErrorResponse() + if not data or not data.get('password'): + return InvalidPayloadErrorResponse('current password is missing') - password_data = data.get('password') - message = check_password(password_data) + current_password = data.get('password') + if not bcrypt.check_password_hash(auth_user.password, current_password): + return UnauthorizedErrorResponse('invalid credentials') + + new_password = data.get('new_password') + message = check_password(new_password) if message != '': return InvalidPayloadErrorResponse(message) - password = bcrypt.generate_password_hash( - password_data, current_app.config.get('BCRYPT_LOG_ROUNDS') + hashed_password = bcrypt.generate_password_hash( + new_password, current_app.config.get('BCRYPT_LOG_ROUNDS') ).decode() try: - auth_user.password = password + auth_user.password = hashed_password db.session.commit() return { diff --git a/fittrackee_client/src/components/User/ProfileEdition/UserAccountEdition.vue b/fittrackee_client/src/components/User/ProfileEdition/UserAccountEdition.vue index 0710be6a..a4dd425d 100644 --- a/fittrackee_client/src/components/User/ProfileEdition/UserAccountEdition.vue +++ b/fittrackee_client/src/components/User/ProfileEdition/UserAccountEdition.vue @@ -18,18 +18,29 @@ +