##############################################################################
#
# Copyright (c) 2003 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Decorator support

Decorators are proxies that are mostly transparent but that may provide
additional features.
"""

from zope.proxy import ProxyBase
from zope.proxy import getProxiedObject
from zope.proxy.decorator import SpecificationDecoratorBase

from zope.security.checker import CombinedChecker
from zope.security.checker import selectChecker
from zope.security.proxy import Proxy
from zope.security.proxy import getChecker


class DecoratedSecurityCheckerDescriptor:
    """Descriptor for a Decorator that provides a decorated security checker.
    """

    def __get__(self, inst, cls=None):
        if inst is None:
            return self
        else:
            proxied_object = getProxiedObject(inst)
            if isinstance(proxied_object, Proxy):
                checker = getChecker(proxied_object)
            else:
                checker = getattr(proxied_object, '__Security_checker__', None)
                if checker is None:
                    checker = selectChecker(proxied_object)
            wrapper_checker = selectChecker(inst)
            if wrapper_checker is None and checker is None:
                raise AttributeError("%r has no attribute %r" %
                                     (proxied_object.__class__.__name__,
                                      '__Security_checker__'))
            elif wrapper_checker is None:
                return checker
            elif checker is None:
                return wrapper_checker
            else:
                return CombinedChecker(wrapper_checker, checker)

    def __set__(self, inst, value):
        raise TypeError("Can't set __Security_checker__ on a decorated object")


class SecurityCheckerDecoratorBase(ProxyBase):
    """Base class for proxy that provides additional security declarations."""

    __Security_checker__ = DecoratedSecurityCheckerDescriptor()


class DecoratorBase(SpecificationDecoratorBase, SecurityCheckerDecoratorBase):
    """Base class for a proxy that provides both additional interfaces and
    security declarations."""


# zope.location was made independent of security. To work together with
# security, we re-inject the DecoratedSecurityCheckerDescriptor onto the
# location proxy from here.
# This is the only sane place we found for doing it: it kicks in as soon
# as someone starts using security proxies.
import zope.location.location  # noqa: E402 module level import not at top


zope.location.location.LocationProxy.__Security_checker__ = (
    DecoratedSecurityCheckerDescriptor())
