|
20 | 20 | import six
|
21 | 21 | from six.moves import urllib
|
22 | 22 |
|
| 23 | +from firebase_admin import exceptions |
| 24 | +from firebase_admin import _utils |
| 25 | + |
23 | 26 |
|
24 | 27 | MAX_CLAIMS_PAYLOAD_SIZE = 1000
|
25 | 28 | RESERVED_CLAIMS = set([
|
@@ -188,3 +191,121 @@ def validate_action_type(action_type):
|
188 | 191 | raise ValueError('Invalid action type provided action_type: {0}. \
|
189 | 192 | Valid values are {1}'.format(action_type, ', '.join(VALID_EMAIL_ACTION_TYPES)))
|
190 | 193 | return action_type
|
| 194 | + |
| 195 | + |
| 196 | +class UidAlreadyExistsError(exceptions.AlreadyExistsError): |
| 197 | + """The user with the provided uid already exists.""" |
| 198 | + |
| 199 | + default_message = 'The user with the provided uid already exists' |
| 200 | + |
| 201 | + def __init__(self, message, cause, http_response): |
| 202 | + exceptions.AlreadyExistsError.__init__(self, message, cause, http_response) |
| 203 | + |
| 204 | + |
| 205 | +class EmailAlreadyExistsError(exceptions.AlreadyExistsError): |
| 206 | + """The user with the provided email already exists.""" |
| 207 | + |
| 208 | + default_message = 'The user with the provided email already exists' |
| 209 | + |
| 210 | + def __init__(self, message, cause, http_response): |
| 211 | + exceptions.AlreadyExistsError.__init__(self, message, cause, http_response) |
| 212 | + |
| 213 | + |
| 214 | +class InvalidDynamicLinkDomainError(exceptions.InvalidArgumentError): |
| 215 | + """Dynamic link domain in ActionCodeSettings is not authorized.""" |
| 216 | + |
| 217 | + default_message = 'Dynamic link domain specified in ActionCodeSettings is not authorized' |
| 218 | + |
| 219 | + def __init__(self, message, cause, http_response): |
| 220 | + exceptions.InvalidArgumentError.__init__(self, message, cause, http_response) |
| 221 | + |
| 222 | + |
| 223 | +class InvalidIdTokenError(exceptions.InvalidArgumentError): |
| 224 | + """The provided ID token is not a valid Firebase ID token.""" |
| 225 | + |
| 226 | + default_message = 'The provided ID token is invalid' |
| 227 | + |
| 228 | + def __init__(self, message, cause=None, http_response=None): |
| 229 | + exceptions.InvalidArgumentError.__init__(self, message, cause, http_response) |
| 230 | + |
| 231 | + |
| 232 | +class PhoneNumberAlreadyExistsError(exceptions.AlreadyExistsError): |
| 233 | + """The user with the provided phone number already exists.""" |
| 234 | + |
| 235 | + default_message = 'The user with the provided phone number already exists' |
| 236 | + |
| 237 | + def __init__(self, message, cause, http_response): |
| 238 | + exceptions.AlreadyExistsError.__init__(self, message, cause, http_response) |
| 239 | + |
| 240 | + |
| 241 | +class UnexpectedResponseError(exceptions.UnknownError): |
| 242 | + """Backend service responded with an unexpected or malformed response.""" |
| 243 | + |
| 244 | + def __init__(self, message, cause=None, http_response=None): |
| 245 | + exceptions.UnknownError.__init__(self, message, cause, http_response) |
| 246 | + |
| 247 | + |
| 248 | +class UserNotFoundError(exceptions.NotFoundError): |
| 249 | + """No user record found for the specified identifier.""" |
| 250 | + |
| 251 | + default_message = 'No user record found for the given identifier' |
| 252 | + |
| 253 | + def __init__(self, message, cause=None, http_response=None): |
| 254 | + exceptions.NotFoundError.__init__(self, message, cause, http_response) |
| 255 | + |
| 256 | + |
| 257 | +_CODE_TO_EXC_TYPE = { |
| 258 | + 'DUPLICATE_EMAIL': EmailAlreadyExistsError, |
| 259 | + 'DUPLICATE_LOCAL_ID': UidAlreadyExistsError, |
| 260 | + 'INVALID_DYNAMIC_LINK_DOMAIN': InvalidDynamicLinkDomainError, |
| 261 | + 'INVALID_ID_TOKEN': InvalidIdTokenError, |
| 262 | + 'PHONE_NUMBER_EXISTS': PhoneNumberAlreadyExistsError, |
| 263 | + 'USER_NOT_FOUND': UserNotFoundError, |
| 264 | +} |
| 265 | + |
| 266 | + |
| 267 | +def handle_auth_backend_error(error): |
| 268 | + """Converts a requests error received from the Firebase Auth service into a FirebaseError.""" |
| 269 | + if error.response is None: |
| 270 | + raise _utils.handle_requests_error(error) |
| 271 | + |
| 272 | + code, custom_message = _parse_error_body(error.response) |
| 273 | + if not code: |
| 274 | + msg = 'Unexpected error response: {0}'.format(error.response.content.decode()) |
| 275 | + raise _utils.handle_requests_error(error, message=msg) |
| 276 | + |
| 277 | + exc_type = _CODE_TO_EXC_TYPE.get(code) |
| 278 | + msg = _build_error_message(code, exc_type, custom_message) |
| 279 | + if not exc_type: |
| 280 | + return _utils.handle_requests_error(error, message=msg) |
| 281 | + |
| 282 | + return exc_type(msg, cause=error, http_response=error.response) |
| 283 | + |
| 284 | + |
| 285 | +def _parse_error_body(response): |
| 286 | + """Parses the given error response to extract Auth error code and message.""" |
| 287 | + error_dict = {} |
| 288 | + try: |
| 289 | + parsed_body = response.json() |
| 290 | + if isinstance(parsed_body, dict): |
| 291 | + error_dict = parsed_body.get('error', {}) |
| 292 | + except ValueError: |
| 293 | + pass |
| 294 | + |
| 295 | + # Auth error response format: {"error": {"message": "AUTH_ERROR_CODE: Optional text"}} |
| 296 | + code = error_dict.get('message') if isinstance(error_dict, dict) else None |
| 297 | + custom_message = None |
| 298 | + if code: |
| 299 | + separator = code.find(':') |
| 300 | + if separator != -1: |
| 301 | + custom_message = code[separator + 1:].strip() |
| 302 | + code = code[:separator] |
| 303 | + |
| 304 | + return code, custom_message |
| 305 | + |
| 306 | + |
| 307 | +def _build_error_message(code, exc_type, custom_message): |
| 308 | + default_message = exc_type.default_message if ( |
| 309 | + exc_type and hasattr(exc_type, 'default_message')) else 'Error while calling Auth service' |
| 310 | + ext = ' {0}'.format(custom_message) if custom_message else '' |
| 311 | + return '{0} ({1}).{2}'.format(default_message, code, ext) |
0 commit comments