From 91cfdd55c9dec69848c3809278af93fe13443780 Mon Sep 17 00:00:00 2001 From: Bassam Ojeil Date: Mon, 3 May 2021 13:35:58 -0700 Subject: [PATCH 1/4] fix(auth): adds missing EMAIL_NOT_FOUND error code Catch EMAIL_NOT_FOUND and translate to EmailNotFoundError when /accounts:sendOobCode is called for password reset on a user that does not exist. --- firebase_admin/_auth_client.py | 2 ++ firebase_admin/_auth_utils.py | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/firebase_admin/_auth_client.py b/firebase_admin/_auth_client.py index 2f6713d41..1d4e45bbf 100644 --- a/firebase_admin/_auth_client.py +++ b/firebase_admin/_auth_client.py @@ -444,6 +444,7 @@ def generate_password_reset_link(self, email, action_code_settings=None): Raises: ValueError: If the provided arguments are invalid + EmailNotFoundError: If no user exists by the specified email address. FirebaseError: If an error occurs while generating the link """ return self._user_manager.generate_email_action_link( @@ -464,6 +465,7 @@ def generate_email_verification_link(self, email, action_code_settings=None): Raises: ValueError: If the provided arguments are invalid + UserNotFoundError: If no user exists by the specified email address. FirebaseError: If an error occurs while generating the link """ return self._user_manager.generate_email_action_link( diff --git a/firebase_admin/_auth_utils.py b/firebase_admin/_auth_utils.py index d8e49b1a1..50c52812e 100644 --- a/firebase_admin/_auth_utils.py +++ b/firebase_admin/_auth_utils.py @@ -351,6 +351,15 @@ def __init__(self, message, cause=None, http_response=None): exceptions.NotFoundError.__init__(self, message, cause, http_response) +class EmailNotFoundError(exceptions.NotFoundError): + """No user record found for the specified email.""" + + default_message = 'No user record found for the given email' + + def __init__(self, message, cause=None, http_response=None): + exceptions.NotFoundError.__init__(self, message, cause, http_response) + + class TenantNotFoundError(exceptions.NotFoundError): """No tenant found for the specified identifier.""" @@ -381,6 +390,7 @@ def __init__(self, message, cause=None, http_response=None): 'DUPLICATE_EMAIL': EmailAlreadyExistsError, 'DUPLICATE_LOCAL_ID': UidAlreadyExistsError, 'EMAIL_EXISTS': EmailAlreadyExistsError, + 'EMAIL_NOT_FOUND': EmailNotFoundError, 'INSUFFICIENT_PERMISSION': InsufficientPermissionError, 'INVALID_DYNAMIC_LINK_DOMAIN': InvalidDynamicLinkDomainError, 'INVALID_ID_TOKEN': InvalidIdTokenError, From 02e10a23dab2ad6f65983055851b4d0ee7b74ed5 Mon Sep 17 00:00:00 2001 From: Bassam Ojeil Date: Mon, 3 May 2021 14:46:11 -0700 Subject: [PATCH 2/4] Adds additional test and exposes new exception on Auth. --- firebase_admin/auth.py | 2 ++ tests/test_user_mgt.py | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/firebase_admin/auth.py b/firebase_admin/auth.py index 5154bb495..ed9829aca 100644 --- a/firebase_admin/auth.py +++ b/firebase_admin/auth.py @@ -39,6 +39,7 @@ 'ConfigurationNotFoundError', 'DELETE_ATTRIBUTE', 'EmailAlreadyExistsError', + 'EmailNotFoundError', 'ErrorInfo', 'ExpiredIdTokenError', 'ExpiredSessionCookieError', @@ -112,6 +113,7 @@ DELETE_ATTRIBUTE = _user_mgt.DELETE_ATTRIBUTE DeleteUsersResult = _user_mgt.DeleteUsersResult EmailAlreadyExistsError = _auth_utils.EmailAlreadyExistsError +EmailNotFoundError = _auth_utils.EmailNotFoundError ErrorInfo = _user_import.ErrorInfo ExpiredIdTokenError = _token_gen.ExpiredIdTokenError ExpiredSessionCookieError = _token_gen.ExpiredSessionCookieError diff --git a/tests/test_user_mgt.py b/tests/test_user_mgt.py index ac80a92a6..35afd21e5 100644 --- a/tests/test_user_mgt.py +++ b/tests/test_user_mgt.py @@ -1445,6 +1445,16 @@ def test_api_call_failure(self, user_mgt_app, func): assert str(excinfo.value) == 'Error while calling Auth service (UNEXPECTED_CODE).' assert excinfo.value.http_response is not None assert excinfo.value.cause is not None + + def test_password_reset_non_existing(self, user_mgt_app): + _instrument_user_manager(user_mgt_app, 400, '{"error":{"message": "EMAIL_NOT_FOUND"}}') + with pytest.raises(auth.EmailNotFoundError) as excinfo: + auth.generate_password_reset_link('nonexistent@user', MOCK_ACTION_CODE_SETTINGS, app=user_mgt_app) + error_msg = 'No user record found for the given email (EMAIL_NOT_FOUND).' + assert excinfo.value.code == exceptions.NOT_FOUND + assert str(excinfo.value) == error_msg + assert excinfo.value.http_response is not None + assert excinfo.value.cause is not None @pytest.mark.parametrize('func', [ auth.generate_sign_in_with_email_link, From 38f0c15e6c57252900c8fe0bd765889fd6a27724 Mon Sep 17 00:00:00 2001 From: Bassam Ojeil Date: Mon, 3 May 2021 14:51:13 -0700 Subject: [PATCH 3/4] Fixes linter errors. --- tests/test_user_mgt.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_user_mgt.py b/tests/test_user_mgt.py index 35afd21e5..10dfe698f 100644 --- a/tests/test_user_mgt.py +++ b/tests/test_user_mgt.py @@ -1445,11 +1445,12 @@ def test_api_call_failure(self, user_mgt_app, func): assert str(excinfo.value) == 'Error while calling Auth service (UNEXPECTED_CODE).' assert excinfo.value.http_response is not None assert excinfo.value.cause is not None - + def test_password_reset_non_existing(self, user_mgt_app): _instrument_user_manager(user_mgt_app, 400, '{"error":{"message": "EMAIL_NOT_FOUND"}}') with pytest.raises(auth.EmailNotFoundError) as excinfo: - auth.generate_password_reset_link('nonexistent@user', MOCK_ACTION_CODE_SETTINGS, app=user_mgt_app) + auth.generate_password_reset_link( + 'nonexistent@user', MOCK_ACTION_CODE_SETTINGS, app=user_mgt_app) error_msg = 'No user record found for the given email (EMAIL_NOT_FOUND).' assert excinfo.value.code == exceptions.NOT_FOUND assert str(excinfo.value) == error_msg From 6691d848572c6f220029078a53b4b23592e21b52 Mon Sep 17 00:00:00 2001 From: Bassam Ojeil Date: Mon, 3 May 2021 17:23:37 -0700 Subject: [PATCH 4/4] Fixes comments on error raising. --- firebase_admin/_auth_client.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firebase_admin/_auth_client.py b/firebase_admin/_auth_client.py index 1d4e45bbf..a58dbef74 100644 --- a/firebase_admin/_auth_client.py +++ b/firebase_admin/_auth_client.py @@ -181,7 +181,7 @@ def get_user_by_email(self, email): Raises: ValueError: If the email is None, empty or malformed. - UserNotFoundError: If no user exists by the specified email address. + UserNotFoundError: If no user exists for the specified email address. FirebaseError: If an error occurs while retrieving the user. """ response = self._user_manager.get_user(email=email) @@ -198,7 +198,7 @@ def get_user_by_phone_number(self, phone_number): Raises: ValueError: If the phone number is ``None``, empty or malformed. - UserNotFoundError: If no user exists by the specified phone number. + UserNotFoundError: If no user exists for the specified phone number. FirebaseError: If an error occurs while retrieving the user. """ response = self._user_manager.get_user(phone_number=phone_number) @@ -444,7 +444,7 @@ def generate_password_reset_link(self, email, action_code_settings=None): Raises: ValueError: If the provided arguments are invalid - EmailNotFoundError: If no user exists by the specified email address. + EmailNotFoundError: If no user exists for the specified email address. FirebaseError: If an error occurs while generating the link """ return self._user_manager.generate_email_action_link( @@ -465,7 +465,7 @@ def generate_email_verification_link(self, email, action_code_settings=None): Raises: ValueError: If the provided arguments are invalid - UserNotFoundError: If no user exists by the specified email address. + UserNotFoundError: If no user exists for the specified email address. FirebaseError: If an error occurs while generating the link """ return self._user_manager.generate_email_action_link(