Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
decorators.py 2.30 KiB
# -*- coding: utf-8 -*-
"""
app.decorators
~~~~~~~~~~~~~~

This module defines some useful decorators.

:copyright: (c) 2017 European Spallation Source ERIC
:license: BSD 2-Clause, see LICENSE for more details.

"""
from functools import wraps
from flask import current_app, abort
from flask_login import current_user
from flask_jwt_extended import get_current_user
from .utils import CSEntryError


def jwt_groups_accepted(*groups):
    """Decorator which specifies that a user must have at least one of the specified groups.

    This shall be used for users logged in using a JWT (API).

    Example::
        @bp.route('/models', methods=['POST'])
        @jwt_required
        @jwt_groups_accepted('admin', 'create')
        def create_model():
            return create()

    The current user must be in either 'admin' or 'create' group
    to access this route.

    :param groups: accepted groups
    """
    def wrapper(fn):
        @wraps(fn)
        def decorated_view(*args, **kwargs):
            user = get_current_user()
            if user is None:
                raise CSEntryError('Invalid indentity', status_code=403)
            if not user.is_member_of_one_group(groups):
                raise CSEntryError("User doesn't have the required group", status_code=403)
            return fn(*args, **kwargs)
        return decorated_view
    return wrapper


def login_groups_accepted(*groups):
    """Decorator which specifies that a user must have at least one of the specified groups.

    This shall be used for users logged in using a cookie (web UI).

    Example::
        @bp.route('/models', methods=['POST'])
        @login_groups_accepted('admin', 'create')
        def create_model():
            return create()

    The current user must be in either 'admin' or 'create' group
    to access this route.
    This checks that the user is logged in. There is no need to
    use the @login_required decorator.

    :param groups: accepted groups
    """
    def wrapper(fn):
        @wraps(fn)
        def decorated_view(*args, **kwargs):
            if not current_user.is_authenticated:
                return current_app.login_manager.unauthorized()
            if not current_user.is_member_of_one_group(groups):
                abort(403)
            return fn(*args, **kwargs)
        return decorated_view
    return wrapper