SCUEvals/scuevals-api

View on GitHub
scuevals_api/auth/decorators.py

Summary

Maintainability
A
1 hr
Test Coverage
from functools import wraps
from flask_jwt_extended import get_jwt_identity, jwt_required, current_user
from flask_jwt_extended.exceptions import JWTExtendedException, UserLoadError, UserClaimsVerificationError
from werkzeug.exceptions import Unauthorized, InternalServerError

from scuevals_api.models import User


def optional_arg_decorator(fn):
    def wrapped_decorator(*args):
        if len(args) == 1 and callable(args[0]):
            return fn(args[0])

        else:
            def real_decorator(decoratee):
                return fn(decoratee, *args)

            return real_decorator

    return wrapped_decorator


@optional_arg_decorator
def auth_required(fn, *permissions):
    """
    Decorating a view with this ensures that the requester provided a JWT
    and that the requester has any of the permissions needed to access the view.
    """
    @wraps(fn)
    def wrapper(*args, **kwargs):

        try:
            jwt_required(lambda: None)()
        except UserLoadError:
            raise InternalServerError('unable to load user')
        except UserClaimsVerificationError:
            raise Unauthorized('invalid or expired user info')
        except JWTExtendedException as ex:
            raise Unauthorized('authorization error: ' + str(ex))

        identity = get_jwt_identity()

        if identity['type'] == User.Normal:
            # fail if the user is still suspended
            if current_user.suspended():
                raise Unauthorized('user is suspended')

        if identity['type'] == User.Student:
            # make sure the read access is synced up
            current_user.check_read_access()

        # verify that the user has the correct permissions for this view
        if permissions and len(set(permissions).intersection(current_user.permissions_list)) == 0:
            raise Unauthorized()

        return fn(*args, **kwargs)
    return wrapper