scuevals_api/auth/decorators.py
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