Source

django-pobject / pobject / decorators.py

Full commit
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from exceptional_middleware.responses import HttpForbidden
from django.contrib.auth.decorators import login_required
from django.utils.decorators import available_attrs
from functools import wraps
import logging
import traceback
from pobject import PermissionExprException, P


def permission_required(perm, *checks):
    """A permission decorator that executes 'checks' in canonical order. If all checks succeed, view function is executed.
    Otherwise, 403 (Forbidden) is returned.

    'checks' is an iterable of callables that returns a boolean or equivalent. A 'check' can be a single function, a P object
    or combination of both so long the return value is a truth value.

    For example,
    # User must be a friend of the current user
    @permission_required(is_friend)

    # User must be an owner of the instance when request method is POST (assuming POST is used for updating a model instance)
    @permission_required(
        P(is_owner, "POST")
    )

    # User must be an owner of the instance and (a member of the group or a friend of current user)
    @permission_required(
        is_owner,
        P(is_group_member) | P(is_friend)
    )
    """
    def wrapper(view_func):
        @login_required
        @wraps(view_func, assigned=available_attrs(view_func))
        def perm_check(request, *args, **kwargs):
            if perm and not request.user.has_perm(perm):
                raise HttpForbidden

            check = None
            try:
                for check in checks:
                    # check must be a callable or an instance of P
                    result = False
                    if isinstance(check, P):
                        result = check.is_successful(request, *args, **kwargs)
                    if callable(check):
                        result = check(request, *args, **kwargs)

                    if not result:
                        logging.debug('%s fails %s permission checks.' % (request.user.username, check))
                        raise HttpForbidden
            except PermissionExprException, e:
                logging.error(traceback.format_exc())
                raise e

            return view_func(request, *args, **kwargs)
        return perm_check
    return wrapper