Beispiel #1
0
def accountFromPrincipal(principal):
    """Adapt ILaunchpadPrincipal to IAccount."""
    if ILaunchpadPrincipal.providedBy(principal):
        return principal.account
    else:
        # This is not actually necessary when this is used as an adapter
        # from ILaunchpadPrincipal, as we know we always have an
        # ILaunchpadPrincipal.
        #
        # When Zope3 interfaces allow returning None for "cannot adapt"
        # we can return None here.
        ##return None
        raise ComponentLookupError
Beispiel #2
0
def accountFromPrincipal(principal):
    """Adapt ILaunchpadPrincipal to IAccount."""
    if ILaunchpadPrincipal.providedBy(principal):
        return principal.account
    else:
        # This is not actually necessary when this is used as an adapter
        # from ILaunchpadPrincipal, as we know we always have an
        # ILaunchpadPrincipal.
        #
        # When Zope3 interfaces allow returning None for "cannot adapt"
        # we can return None here.
        ##return None
        raise ComponentLookupError
Beispiel #3
0
def person_from_principal(principal):
    """Adapt `ILaunchpadPrincipal` to `IPerson`."""
    if ILaunchpadPrincipal.providedBy(principal):
        if principal.person is None:
            raise ComponentLookupError
        return principal.person
    else:
        # This is not actually necessary when this is used as an adapter
        # from ILaunchpadPrincipal, as we know we always have an
        # ILaunchpadPrincipal.
        #
        # When Zope3 interfaces allow returning None for "cannot adapt"
        # we can return None here.
        ##return None
        raise ComponentLookupError
Beispiel #4
0
def person_from_principal(principal):
    """Adapt `ILaunchpadPrincipal` to `IPerson`."""
    if ILaunchpadPrincipal.providedBy(principal):
        if principal.person is None:
            raise ComponentLookupError
        return principal.person
    else:
        # This is not actually necessary when this is used as an adapter
        # from ILaunchpadPrincipal, as we know we always have an
        # ILaunchpadPrincipal.
        #
        # When Zope3 interfaces allow returning None for "cannot adapt"
        # we can return None here.
        ##return None
        raise ComponentLookupError
Beispiel #5
0
def iter_authorization(objecttoauthorize, permission, principal, cache,
                       breadth_first=True):
    """Work through `IAuthorization` adapters for `objecttoauthorize`.

    Adapters are permitted to delegate checks to other adapters, and this
    manages that delegation such that the minimum number of checks are made,
    subject to a breadth-first check of delegations.

    This also updates `cache` as it goes along, though `cache` can be `None`
    if no caching is desired. Only leaf values are cached; the results of a
    delegated authorization are not cached.
    """
    # Check if this calculation has already been done.
    if cache is not None and objecttoauthorize in cache:
        if permission in cache[objecttoauthorize]:
            # Result cached => yield and return.
            yield cache[objecttoauthorize][permission]
            return

    # Create a check_auth function to call checkAuthenticated or
    # checkUnauthenticated as appropriate.
    if ILaunchpadPrincipal.providedBy(principal):
        check_auth = lambda authorization: (
            authorization.checkAuthenticated(IPersonRoles(principal.person)))
    else:
        check_auth = lambda authorization: (
            authorization.checkUnauthenticated())

    # Each entry in queue should be an iterable of (object, permission)
    # tuples, upon which permission checks will be performed.
    queue = deque()
    enqueue = (queue.append if breadth_first else queue.appendleft)

    # Enqueue the starting object and permission.
    enqueue(((objecttoauthorize, permission),))

    while len(queue) != 0:
        for obj, permission in queue.popleft():
            # Unwrap object; see checkPermission for why.
            obj = removeAllProxies(obj)
            # First, check the cache.
            if cache is not None:
                if obj in cache and permission in cache[obj]:
                    # Result cached => yield and skip to the next.
                    yield cache[obj][permission]
                    continue
            # Get an IAuthorization for (obj, permission).
            authorization = queryAdapter(obj, IAuthorization, permission)
            if authorization is None:
                # No authorization adapter => denied.
                yield False
                continue
            # We have an authorization adapter, so check it. This is one of
            # the possibly-expensive bits that a cache can help with.
            result = check_auth(authorization)
            # Is the authorization adapter delegating to other objects?
            if isinstance(result, Iterable):
                enqueue(result)
                continue
            # We have a non-delegated result.
            if result is not True and result is not False:
                warnings.warn(
                    '%r returned %r (%r)' % (
                        authorization, result, type(result)))
                result = bool(result)
            # Update the cache if one has been provided.
            if cache is not None:
                if obj in cache:
                    cache[obj][permission] = result
                else:
                    cache[obj] = {permission: result}
            # Let the world know.
            yield result
Beispiel #6
0
    def checkPermission(self, permission, object):
        """Check the permission, object, user against the launchpad
        authorization policy.

        If the object is a view, then consider the object to be the view's
        context.

        If we are running in read-only mode, all permission checks are
        failed except for launchpad.View requests, which are checked
        as normal. All other permissions are used to protect write
        operations.

        Workflow:
        - If the principal is not None and its access level is not what is
          required by the permission, deny.
        - If the object to authorize is private and the principal has no
          access to private objects, deny.
        - If we have zope.Public, allow.  (But we shouldn't ever get this.)
        - If we have launchpad.AnyPerson and the principal is an
          ILaunchpadPrincipal then allow.
        - If the object has an IAuthorization named adapter, named
          after the permission, use that to check the permission.
        - Otherwise, deny.
        """
        # If we have a view, get its context and use that to get an
        # authorization adapter.
        if IView.providedBy(object):
            objecttoauthorize = object.context
        else:
            objecttoauthorize = object
        if objecttoauthorize is None:
            # We will not be able to lookup an adapter for this, so we can
            # return False already.
            return False
        # Remove all proxies from object to authorize. The security proxy is
        # removed for obvious reasons but we also need to remove the location
        # proxy (which is used on apidoc.lp.dev) because otherwise we can't
        # create a weak reference to our object in our security policy cache.
        objecttoauthorize = removeAllProxies(objecttoauthorize)

        participations = [
            participation for participation in self.participations
            if participation.principal is not system_user]

        if len(participations) > 1:
            raise RuntimeError("More than one principal participating.")

        # The participation's cache of (object -> permission -> result), or
        # None if the participation does not support caching.
        participation_cache = None
        # A cache of (permission -> result) for objecttoauthorize, or None if
        # the participation does not support caching. This resides as a value
        # of participation_cache.
        object_cache = None

        if len(participations) == 0:
            principal = None
        else:
            participation = participations[0]
            if IApplicationRequest.providedBy(participation):
                participation_cache = participation.annotations.setdefault(
                    LAUNCHPAD_SECURITY_POLICY_CACHE_KEY,
                    weakref.WeakKeyDictionary())
                object_cache = participation_cache.setdefault(
                    objecttoauthorize, {})
                if permission in object_cache:
                    return object_cache[permission]
            principal = removeAllProxies(participation.principal)

        if (principal is not None and
            not isinstance(principal, UnauthenticatedPrincipal)):
            access_level = self._getPrincipalsAccessLevel(
                principal, objecttoauthorize)
            if not self._checkRequiredAccessLevel(
                access_level, permission, objecttoauthorize):
                return False
            if not self._checkPrivacy(access_level, objecttoauthorize):
                return False

        # The following two checks shouldn't be needed, strictly speaking,
        # because zope.Public is CheckerPublic, and the Zope security
        # machinery shortcuts this to always allow it. However, it is here as
        # a "belt and braces". It is also a bit of a lie: if the permission is
        # zope.Public, privacy and access levels (checked above) will be
        # irrelevant!
        if permission == 'zope.Public':
            return True
        if permission is CheckerPublic:
            return True

        if (permission == 'launchpad.AnyPerson' and
            ILaunchpadPrincipal.providedBy(principal)):
            return True

        # If there are delegated authorizations they must *all* be allowed
        # before permission to access objecttoauthorize is granted.
        result = all(
            iter_authorization(
                objecttoauthorize, permission, principal,
                participation_cache, breadth_first=True))

        # Cache the top-level result. Be warned that this result /may/ be
        # based on 10s or 100s of delegated authorization checks, and so even
        # small changes in the model data could invalidate this result.
        if object_cache is not None:
            object_cache[permission] = result

        return result
def iter_authorization(objecttoauthorize, permission, principal, cache, breadth_first=True):
    """Work through `IAuthorization` adapters for `objecttoauthorize`.

    Adapters are permitted to delegate checks to other adapters, and this
    manages that delegation such that the minimum number of checks are made,
    subject to a breadth-first check of delegations.

    This also updates `cache` as it goes along, though `cache` can be `None`
    if no caching is desired. Only leaf values are cached; the results of a
    delegated authorization are not cached.
    """
    # Check if this calculation has already been done.
    if cache is not None and objecttoauthorize in cache:
        if permission in cache[objecttoauthorize]:
            # Result cached => yield and return.
            yield cache[objecttoauthorize][permission]
            return

    # Create a check_auth function to call checkAuthenticated or
    # checkUnauthenticated as appropriate.
    if ILaunchpadPrincipal.providedBy(principal):
        check_auth = lambda authorization: (authorization.checkAuthenticated(IPersonRoles(principal.person)))
    else:
        check_auth = lambda authorization: (authorization.checkUnauthenticated())

    # Each entry in queue should be an iterable of (object, permission)
    # tuples, upon which permission checks will be performed.
    queue = deque()
    enqueue = queue.append if breadth_first else queue.appendleft

    # Enqueue the starting object and permission.
    enqueue(((objecttoauthorize, permission),))

    while len(queue) != 0:
        for obj, permission in queue.popleft():
            # Unwrap object; see checkPermission for why.
            obj = removeAllProxies(obj)
            # First, check the cache.
            if cache is not None:
                if obj in cache and permission in cache[obj]:
                    # Result cached => yield and skip to the next.
                    yield cache[obj][permission]
                    continue
            # Get an IAuthorization for (obj, permission).
            authorization = queryAdapter(obj, IAuthorization, permission)
            if authorization is None:
                # No authorization adapter => denied.
                yield False
                continue
            # We have an authorization adapter, so check it. This is one of
            # the possibly-expensive bits that a cache can help with.
            result = check_auth(authorization)
            # Is the authorization adapter delegating to other objects?
            if isinstance(result, Iterable):
                enqueue(result)
                continue
            # We have a non-delegated result.
            if result is not True and result is not False:
                warnings.warn("%r returned %r (%r)" % (authorization, result, type(result)))
                result = bool(result)
            # Update the cache if one has been provided.
            if cache is not None:
                if obj in cache:
                    cache[obj][permission] = result
                else:
                    cache[obj] = {permission: result}
            # Let the world know.
            yield result
    def checkPermission(self, permission, object):
        """Check the permission, object, user against the launchpad
        authorization policy.

        If the object is a view, then consider the object to be the view's
        context.

        If we are running in read-only mode, all permission checks are
        failed except for launchpad.View requests, which are checked
        as normal. All other permissions are used to protect write
        operations.

        Workflow:
        - If the principal is not None and its access level is not what is
          required by the permission, deny.
        - If the object to authorize is private and the principal has no
          access to private objects, deny.
        - If we have zope.Public, allow.  (But we shouldn't ever get this.)
        - If we have launchpad.AnyPerson and the principal is an
          ILaunchpadPrincipal then allow.
        - If the object has an IAuthorization named adapter, named
          after the permission, use that to check the permission.
        - Otherwise, deny.
        """
        # If we have a view, get its context and use that to get an
        # authorization adapter.
        if IView.providedBy(object):
            objecttoauthorize = object.context
        else:
            objecttoauthorize = object
        if objecttoauthorize is None:
            # We will not be able to lookup an adapter for this, so we can
            # return False already.
            return False
        # Remove all proxies from object to authorize. The security proxy is
        # removed for obvious reasons but we also need to remove the location
        # proxy (which is used on apidoc.lp.dev) because otherwise we can't
        # create a weak reference to our object in our security policy cache.
        objecttoauthorize = removeAllProxies(objecttoauthorize)

        participations = [
            participation for participation in self.participations if participation.principal is not system_user
        ]

        if len(participations) > 1:
            raise RuntimeError("More than one principal participating.")

        # The participation's cache of (object -> permission -> result), or
        # None if the participation does not support caching.
        participation_cache = None
        # A cache of (permission -> result) for objecttoauthorize, or None if
        # the participation does not support caching. This resides as a value
        # of participation_cache.
        object_cache = None

        if len(participations) == 0:
            principal = None
        else:
            participation = participations[0]
            if IApplicationRequest.providedBy(participation):
                participation_cache = participation.annotations.setdefault(
                    LAUNCHPAD_SECURITY_POLICY_CACHE_KEY, weakref.WeakKeyDictionary()
                )
                object_cache = participation_cache.setdefault(objecttoauthorize, {})
                if permission in object_cache:
                    return object_cache[permission]
            principal = removeAllProxies(participation.principal)

        if principal is not None and not isinstance(principal, UnauthenticatedPrincipal):
            access_level = self._getPrincipalsAccessLevel(principal, objecttoauthorize)
            if not self._checkRequiredAccessLevel(access_level, permission, objecttoauthorize):
                return False
            if not self._checkPrivacy(access_level, objecttoauthorize):
                return False

        # The following two checks shouldn't be needed, strictly speaking,
        # because zope.Public is CheckerPublic, and the Zope security
        # machinery shortcuts this to always allow it. However, it is here as
        # a "belt and braces". It is also a bit of a lie: if the permission is
        # zope.Public, privacy and access levels (checked above) will be
        # irrelevant!
        if permission == "zope.Public":
            return True
        if permission is CheckerPublic:
            return True

        if permission == "launchpad.AnyPerson" and ILaunchpadPrincipal.providedBy(principal):
            return True

        # If there are delegated authorizations they must *all* be allowed
        # before permission to access objecttoauthorize is granted.
        result = all(
            iter_authorization(objecttoauthorize, permission, principal, participation_cache, breadth_first=True)
        )

        # Cache the top-level result. Be warned that this result /may/ be
        # based on 10s or 100s of delegated authorization checks, and so even
        # small changes in the model data could invalidate this result.
        if object_cache is not None:
            object_cache[permission] = result

        return result