summaryrefslogtreecommitdiff
path: root/src/zope/security/adapter.py
blob: 19057103097da09f518c915f6757c82fb757dd7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
##############################################################################
#
# Copyright (c) 2004 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.
#
##############################################################################
"""Support for taking security into account in adaptation
"""

from zope.location import ILocation
from zope.location import LocationProxy

from zope.security.checker import ProxyFactory
from zope.security.proxy import removeSecurityProxy


def assertLocation(adapter, parent):
    """
    Assert locatable adapters.

    This function asserts that the adapter get location-proxied if it
    doesn't provide :class:`zope.location.interfaces.ILocation`
    itself. Furthermore, the returned locatable adapter get its parent
    set if its ``__parent__`` attribute is currently None.
    """
    # handle none-locatable adapters (A)
    if not ILocation.providedBy(adapter):
        locatable = LocationProxy(adapter)
        locatable.__parent__ = parent
        return locatable

    # handle locatable, parentless adapters (B)
    if adapter.__parent__ is None:
        adapter.__parent__ = parent
        return adapter

    # handle locatable, parentful adapters (C)
    return adapter


class LocatingTrustedAdapterFactory:
    """
    Adapt an adapter factory to provide trusted and (locatable) adapters.

    Trusted adapters always adapt unproxied objects. If asked to
    adapt any proxied objects, it will unproxy them and then
    security-proxy the resulting adapter (S) unless the objects where not
    security-proxied before (N).

    Further locating trusted adapters provide a location for protected
    adapters only (S). If such a protected adapter itself does not
    provide ILocation it is wrapped within a location proxy and it
    parent will be set. If the adapter does provide
    :class:`zope.location.interfaces.ILocation` and its
    ``__parent__`` is None, we set the ``__parent__`` to the adapter's
    context.
    """

    def __init__(self, factory):
        self.factory = factory
        self.__name__ = factory.__name__
        self.__module__ = factory.__module__

    # protected methods
    def _customizeProtected(self, adapter, context):
        return assertLocation(adapter, context)

    def _customizeUnprotected(self, adapter, context):
        if ILocation.providedBy(adapter) and adapter.__parent__ is None:
            adapter.__parent__ = context
        return adapter

    def __call__(self, *args):
        for arg in args:
            if removeSecurityProxy(arg) is not arg:
                args = [removeSecurityProxy(x) for x in args]
                adapter = self.factory(*args)
                adapter = self._customizeProtected(adapter, args[0])
                return ProxyFactory(adapter)

        adapter = self.factory(*args)
        adapter = self._customizeUnprotected(adapter, args[0])
        return adapter


class TrustedAdapterFactory(LocatingTrustedAdapterFactory):
    """
    Adapt an adapter factory to provide trusted adapters.

    Trusted adapters always adapt unproxied objects. If asked to
    adapt any proxied objects, it will unproxy them and then
    security-proxy the resulting adapter unless the objects where not
    security-proxied before.

    If the adapter does provide
    :class:`zope.location.interfaces.ILocation` and its ``__parent__``
    is None, we set the ``__parent__`` to the adapter's context.
    """

    # do not location-proxy the adapter
    def _customizeProtected(self, adapter, context):
        return self._customizeUnprotected(adapter, context)


class LocatingUntrustedAdapterFactory:
    """
    Adapt an adapter factory to provide locatable untrusted adapters

    Untrusted adapters always adapt proxied objects. If any permission
    other than :const:`zope.Public
    <zope.security.interfaces.PUBLIC_PERMISSION_NAME>` is required,
    untrusted adapters need a location in order that the local
    authentication mechanism can be invoked correctly.

    If the adapter does not provide
    :class:`zope.location.interfaces.ILocation`, we location proxy it
    and set the parent. If the adapter does provide ``ILocation`` and
    its ``__parent__`` is None, we set the ``__parent__`` to the
    adapter's context only.
    """

    def __init__(self, factory):
        self.factory = factory
        self.__name__ = factory.__name__
        self.__module__ = factory.__module__

    def __call__(self, *args):
        adapter = self.factory(*args)
        return assertLocation(adapter, args[0])