Source code for medmij_oauth.exceptions.exceptions

'''
    Module for handling OAuth related errors
'''
import urllib.parse
import json

from enum import (
    Enum,
    auto
)

from http import HTTPStatus

[docs]class ERRORS(Enum): """ Error codes enum to be used as error_code for instantiation of OAuthException Usage example: .. code:: python raise OAuthException(ERRORS.UNAUTHORIZED_CLIENT, 'no such resource', ...) """ INVALID_REQUEST = auto() ACCESS_DENIED = auto() UNAUTHORIZED_CLIENT = auto() UNSUPPORTED_RESPONSE_TYPE = auto() INVALID_SCOPE = auto() SERVER_ERROR = auto() TEMPORARILY_UNAVAILABLE = auto() INVALID_CLIENT = auto() INVALID_GRANT = auto() UNSUPPORTED_GRANT_TYPE = auto()
_ERRORS = { ERRORS.INVALID_REQUEST: { "error": "invalid_request", "status_code": HTTPStatus.BAD_REQUEST }, ERRORS.ACCESS_DENIED: { "error": "access_denied", "status_code": HTTPStatus.BAD_REQUEST }, ERRORS.UNAUTHORIZED_CLIENT: { "error": "unauthorized_client", "status_code": HTTPStatus.UNAUTHORIZED }, ERRORS.UNSUPPORTED_RESPONSE_TYPE: { "error": "unsupported_response_type", "status_code": HTTPStatus.BAD_REQUEST }, ERRORS.INVALID_SCOPE: { "error": "invalid_scope", "status_code": HTTPStatus.BAD_REQUEST }, ERRORS.SERVER_ERROR: { "error": "server_error", "status_code": HTTPStatus.INTERNAL_SERVER_ERROR }, ERRORS.TEMPORARILY_UNAVAILABLE: { "error": "temporarily_unavailable", "status_code": HTTPStatus.SERVICE_UNAVAILABLE }, ERRORS.INVALID_CLIENT: { "error": "invalid_client", "status_code": HTTPStatus.BAD_REQUEST }, ERRORS.INVALID_GRANT: { "error": "invalid_grant", "status_code": HTTPStatus.BAD_REQUEST }, ERRORS.UNSUPPORTED_GRANT_TYPE: { "error": "unsupported_grant_type", "status_code": HTTPStatus.BAD_REQUEST } }
[docs]def lookup_error_code(error): """ Lookup error code by text. When an oauth client receives a error response, it can reproduce the exception by looking up the error code with the 'error' query param that it received. Raises a ValueError if the error passed to it is unknown. Example: .. code:: python error = query_params.get('error') error_description = query_params.get('error_description') raise OAuthException(error_code=lookup_error_code(error), error_description=error_description) """ try: return [code for code, value in _ERRORS.items() if value['error'] == error][0] except IndexError: raise ValueError(f'Unknown error: \'{error}\'')
[docs]class OAuthException(BaseException): """ OAuthException class, represents a oauth error as described in `rfc6749 <https://tools.ietf.org/html/rfc6749#section-4.1.1>`__ :type error_code: :exc:`error code <medmij_oauth.exceptions.ERRORS>` :param error_code: Int that represents a type of error :type error_description: string :param error_description: Human readable description of the error e.g. 'no such resource' :type redirect: bool :param redirect: Indication if on handling of the exception the user should be redirected back to the client application of if the error should be rendered to the screen :type base_redirect_url: string (optional) :param base_redirect_url: The base of the redirect url (on redirection the error params are appended to the base_redirect_url as a query string) Usage examples: .. code:: python raise OAuthException(ERRORS.INVALID_REQUEST, error_description='Invalid redirect url', redirect=False) .. code:: python raise OAuthException(ERRORS.UNAUTHORIZED_CLIENT, error_description='No such resource', redirect=True, base_redirect_url='https://oauthclient.com') """ def __init__( self, error_code, error_description='', redirect=False, base_redirect_url='' ): error = _ERRORS[error_code] super(OAuthException, self).__init__(error_description) self.error_code = error_code self.error_description = error_description self.status_code = error["status_code"] self.error = error["error"] self.redirect = redirect self.base_redirect_url = base_redirect_url
[docs] def get_redirect_url(self): """ Return redirect url to which the end user should be redirected. The redirect_url constists of two parts, self.base_redirect_url and a query string that contains the error and error description Raises a Exception if self.direct != True or if self.base_redirect_url is not set. e.g. https://oauthclient.com/cb/?error=unauthorized_client&error_description=No%20such%20resource """ if not self.redirect or not self.base_redirect_url: raise Exception('Not allowed to get redirect_url if redirect \ is False or base_redirect_url is not set') error_query_string = urllib.parse.urlencode({ 'error': self.error, 'error_description': self.error_description }) return f'{self.base_redirect_url}?{error_query_string}'
[docs] def get_json(self): """ Return json representation of the exception that is targeted at the end user Included properties are 'error' and 'error_description' .. code:: python { 'error': 'unauthorized_client', 'error_description': 'no such resource' } """ return json.dumps(self.get_dict())
[docs] def get_dict(self): """ Return dict representation of the exception that is targeted at the end user. Included properties are 'error' and 'error_description'. """ return { 'error': self.error, 'error_description': self.error_description }
def __str__(self): return self.__repr__() def __repr__(self): return f'OAuthException(error_code={self.error_code}, error_description="{self.error_description}")'