[Zope-CMF] PortalContent permissions

Shane Hathaway shane@digicool.com
Thu, 19 Apr 2001 15:17:00 -0400 (EDT)


On Thu, 19 Apr 2001, seb bacon wrote:
> Hmm, well I tried implementing this, but keep coming up against
> problems too great for me to wrap my brains round.  The problem is
> that whenever I try to check the user's permissions inside
> _index_html, the user checked against is always 'Anonymous User'.  I
> think this is something to do with not acquiring the correct security
> context, but I'm not sure.  I'm in way over my head here, but I'd
> really like to get this implemented...any help appreciated :-)

I see now why this won't quite work.  ZPublisher works in this order:

1. Finds the requested object.
2. Finds the innermost user folder that applies to the object.
3. Authenticates the user, then checks whether the user is allowed to
access the object.
4. traverse() returns the object.
5. publish_module then uses mapply() to process the object.

The _index_html computation occurs at step 1.  We would need to be able to
insert logic between steps 3 and 5.

Perhaps we should try something else: mapply() looks for specific
attributes to discover the function signature and there are places
throughout Zope that fake function signatures for mapply.  If index_html
manifested itself as an object with a computed function signature, it
could add logic to step 5.  index_html's apparent function signature would
be the function signature of the skinned object (which in turn is nearly
always faked as well... sigh.)

I've included a rough cut below based on your code.

Shane


-------- 8-< ---------

from ExtensionClass import Base  # Makes ComputedAttributes work
from AccessControl import getSecurityManager

(...)

class PortalContent:

    (...)

    def _index_html(self):
        '''
        Invokes the action identified by the id "view" or the first
action.
        '''
        return SkinnedDefaultView(self)

    security.declareProtected(CMFCorePermissions.View, 'index_html')
    index_html = ComputedAttribute(_index_html, 1)

    (...)


class SkinnedDefaultView (Base):
    '''
    Invokes the right skinned view after authentication is performed,
    providing mapply() with the info it is seeking.
    '''

    def __init__(self, ob):
        # Avoid mixing up acquisition.
        self.__dict__['_ob'] = ob

    def _findSkinnedOb(self):
        ob = self.__dict__['_ob']
        ti = ob.getTypeInfo()
        if ti is not None:
            path = ti.getActionById('view', None)
            if path is not None:
                view = ob.restrictedTraverse(path)
                return view
            actions = ti.getActions()
            if actions:
                sm = None
                for action in actions:
                    verified = 0
                    permissions = action.get('permissions', None)
                    if not permissions:
                        # This action requires no extra permissions.
                        verified = 1
                    else:
                        if sm is None: sm = getSecurityManager()
                        for permission in permissions:
                            # The user must be able to match at least
                            # one of the listed permissions.
                            if sm.checkPermission(permission, ob):
                                verified = 1
                                break
                    if verified:
                        path = action['action']
                        view = self.restrictedTraverse(path)
                        return view
            raise 'Not Found', ('No default view defined for type "%s"'
                                % ti.getId())
        else:
            raise 'Not Found', ('Cannot find default view for "%s"'
                                % string.join( ob.getPhysicalPath() ) )

    def _getSkinnedOb(self):
        # Caches the object.
        s = self.__dict__.get('_skinob', None)
        if s is not None:
            return s
        s = self._findSkinnedOb()
        self.__dict__['_skinob'] = s
        return s

    def _func_code(self):
        return self._getSkinnedOb().func_code
    func_code = ComputedAttribute(_func_code, 1)

    def __call__(self, *args, **kw):
        return apply(self._getSkinnedOb(), args, kw)