zope.component, browser:addMenuItem, ZCML factory => ForbiddenAttribute: ('__call__', <...>)

Bug #272595 reported by KLEIN Stéphane
0
Affects Status Importance Assigned to Milestone
Zope 3
Won't Fix
Undecided
Unassigned
zope.component
Won't Fix
Undecided
Unassigned

Bug Description

> Hi,
>
> I had some difficulty to use "browser:addMenuItem" ZCML directive :
>
> <browser:addMenuItem
> factory="myproject.MyContent"
> title="MyContent"
> permission="zope.ManageContent"
> />
>
> with my Factory :
>
> <utility
> factory=".mycontent.MyContentFactory"
> name="myproject.MyContent"
> permission="zope.Public"
> />
>
> mycontent.py file :
>
> from zope.component.interfaces import IFactory
>
> ...
>
> class MyContentFactory(object):
> implements(IFactory)
>
> title = u"Create a new MyContent"
> description = u"This factory instantiates new MyContent"
>
> def __call__(self):
> return MyContent()
>
> def getInterfaces(self):
> return implementedBy(MyContent)
>
> Now, if I try to append a MyContent object through ZMI I've this error :
>
> File "/home/harobed/buildout-eggs/zope.app.container-3.6.0-py2.4-linux-
> i686.egg/zope/app/container/browser/adding.py", line 145, in action
> content = factory()
> File "/home/harobed/buildout-eggs/zope.security-3.5.2-py2.4-linux-
> i686.egg/zope/security/checker.py", line 463, in check
> self._checker2.check(object, name)
> ForbiddenAttribute: ('__call__', <myproject.mycontent.MyContentFactory
> object at 0x93f5b0c>)
>
> Well, if I look in zope/app/container/browser/adding.py, in Adding.action
> method I see this comment :
>
> # TODO: If the factory wrapped by LocationProxy is already a
> Proxy,
> # then ProxyFactory does not do the right thing and the
> # original's checker info gets lost. No factory that was
> # registered via ZCML and was used via addMenuItem worked
> # here. (SR)
>
> I think "No factory that was registered via ZCML and was used via
> addMenuItem worked here" speak about my issue ?
>
> I've looked in zope.app.container browser test and I found this tip :
>
> mycontent.py file fixed with the tip :
>
> from zope.component.interfaces import IFactory
> import zope.security.checker
>
> ...
>
> class MyContentFactory(object):
> implements(IFactory)
>
> title = u"Create a new MyContent"
> description = u"This factory instantiates new MyContent"
>
> def __init__(self):
> self.__Security_checker__ = zope.security.checker.NamesChecker
> (['__call__'])
>
> def __call__(self):
> return MyContent()
>
> def getInterfaces(self):
> return implementedBy(MyContent)
>
> With this __Security_checker__ property my issue is fixed, now I can add
> a MyContent object through ZMI.
>
> Now, I've some comment and question about it :
>
> * I think this tip isn't "developer friendly", what is the good method ?
>
> * Philipp von Weitershausen's book (Web Component Development with Zope
> 3) give first version of my example (without __Security_checker__ tip)
> and its example didn't working with zope 3.4 (zopeproject use). Why this
> compatibility breaking ?

Well, I've this problem with zope.component 3.5.1, it's unstable version. It's a
answer to "Why this compatibility breaking". I think it's a bug.

I know what revision bring the issue : http://svn.zope.org/zope.component/trunk/src/zope/component/zcml.py?rev=88794&view=rev

Before the utility function in zope/component/zcml.py is :

def utility(_context, provides=None, component=None, factory=None,
            permission=None, name=''):
    if factory:
        if component:
            raise TypeError("Can't specify factory and component.")
        component = factory()

    if provides is None:
        if factory:
            provides = list(zope.interface.implementedBy(factory))
        else:
            provides = list(zope.interface.providedBy(component))
        if len(provides) == 1:
            provides = provides[0]
        else:
            raise TypeError("Missing 'provides' attribute")

    if permission is not None:
        if permission == PublicPermission:
            permission = CheckerPublic
        checker = InterfaceChecker(provides, permission)

        component = proxify(component, checker)

    _context.action(
        discriminator = ('utility', provides, name),
        callable = handler,
        args = ('registerUtility', component, provides, name)
        )
    _context.action(
        discriminator = None,
        callable = provideInterface,
        args = (provides.__module__ + '.' + provides.getName(), provides)
        )

now it's :

def utility(_context, provides=None, component=None, factory=None,
            permission=None, name=''):
    if factory and component:
        raise TypeError("Can't specify factory and component.")

    if provides is None:
        if factory:
            provides = list(zope.interface.implementedBy(factory))
        else:
            provides = list(zope.interface.providedBy(component))
        if len(provides) == 1:
            provides = provides[0]
        else:
            raise TypeError("Missing 'provides' attribute")

    if permission is not None:
        if permission == PublicPermission:
            permission = CheckerPublic
        checker = InterfaceChecker(provides, permission)

        component = proxify(component, checker)

    _context.action(
        discriminator = ('utility', provides, name),
        callable = handler,
        args = ('registerUtility', component, provides, name),
        kw = dict(factory=factory),
        )
    _context.action(
        discriminator = None,
        callable = provideInterface,
        args = (provides.__module__ + '.' + provides.getName(), provides)
        )

So, in new version, if factory is given then permission isn't used because
component is None. Before, component was builded with factory and permission was
applied on this component and this ZCML declaration worked perfeclty with
"browser:addMenuItem".

Now, I don't know how can I fix this issue with new Utility ZMCL design.

Regards,
Stephane

Tres Seaver (tseaver)
Changed in zope3:
status: New → Won't Fix
Revision history for this message
Tres Seaver (tseaver) wrote :
Changed in zope.component:
status: New → Won't Fix
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.