[TransWarp] Building PEAK apps with Zope X3
Phillip J. Eby
pje at telecommunity.com
Thu Apr 3 21:50:18 EST 2003
These are just notes/observations/opinions about the likely architecture
PEAK will support for Zope X3 applications...
PEAK will supply a custom Publication class optimized for use with PEAK, to
replace the DefaultPublication and/or zope.app.publication objects supplied
with Zope X3. DefaultPublication doesn't do enough, and z.a.p is
ZODB-centric, expecting to obtain the application root object via a ZODB
connection, and using the ZODB transaction system. PEAK's publication
object will use PEAK transactions, and the Publication object will be a
component that you bind to your application root (perhaps as a utility).
zope.app.security is way overkill, but zope.security is actually pretty
cool. We'll want to replace its "management" infrastructure (which, like
ZODB transactions, depend on per-thread singletons) but its base machinery
of proxies, checkers, and permissions IDs looks pretty solid. Alas, we
will probably have to replace the entire 'zope.security.checker' module in
order to replace the "management" infrastructure, unless I come up with a
clever yet acceptable patch... Too bad we can't use module inheritance on
'zope.security.management' deals with the special 'securityManager' and
'securityPolicy' objects, and the mechanisms for replacing them. These
objects are per-thread and per-interpreter, respectively, but there's
little reason for them to be. In PEAK architecture, these should be
contextual utility components, like anything else. Instead of accessing
these as singletons, the checker objects should be components, carrying
their manager and policy in context as normal for a PEAK component.
From the point of view of a checker, all it needs is the ability to check
whether a permission is or isn't currently allowed relative to a given
Security of "Untrusted Code"
Zope wants to be secure for running "untrusted" code. The typical PEAK app
couldn't care less; it's 100% trusted code. (Ty and I in fact have run
into problems with code ownership on Zope 2 apps; code accidentally "owned"
by a developer who left the company shut down a production system when we
revoked the developer's login!)
Anyway, 'zope.security.manager' has a user/executable stack for keeping
track of whose code is actually executing at any given moment. I don't
think we need or want this; I'm tempted to set things up so that a
Principal manages its permissions (i.e., checkers would just ask the
Principal if it has permission to do something). This should be plenty
flexible enough; its "principal" weakness, however, is in being able to
easily set global policies. So I guess we should have a security manager
object, which then delegates to the principal, which in turn delegates to
the object being checked. This is pretty much the pattern we used for
LoginManager-based applications, and it works fairly well.
Zope X3's basic security model looks really pretty good, once you scrape
off the CMS-oriented bells and whistles. If we follow the model of
wrapping the application object in a zope.security.proxy, and from there
every other object is also wrapped, then any ZPT or DTML page that runs is
going to also be working with a security proxied object. Even Python code
in views. This means that even if one is granted access to a view, it
doesn't mean the view itself will be able to access things. If we need a
view to be able to run "setuid" in some sense, we'll have to explicitly
unwrap the proxy to get at otherwise inaccessible things. But this is
good: it ensures that we'll have a consistent security policy, that's
checkable for these kinds of issues. If one has code that unwraps proxies,
there'd better be some kind of parameter validation taking place at the
Proxy execution might be slow. The default checkers check permission
dynamically, all the time. But, if we aren't implementing "ownerous"
permissions, we could implement fast, caching checkers. On the bright
side, the proxied objects don't have a proxied self, so any code they
execute is against unproxied objects. This should tend to reward moving as
much logic as possible into the business objects. In essence, only
method/attribute accesses that are issued by view and templating code will
be checked by the proxies. And of course, checks for each level of object
We should probably have a 'peak.security' package to hold all of our
infrastructure replacements and extensions, along with their interfaces.
Checking Permissions and Roles
Principals should know their own "placeless" roles and permissions. If a
principal is granted or denied a permission directly, the checking is
done. Otherwise, it should ask the subject whether it is allowed the
In order for the principal to know whether it has the permission, it will
need to map the permission to two sets of roles: those granted the
permission, and those denied it. This mapping should be via a utility
retrieved from the context of the subject. If the principal is directly
granted or denied the permission (as part of its definition or state), or
the permission is granted or denied to a role possessed by the principal
(according to its definition or state), then the principal need not consult
the subject further.
However, in all other cases (which I expect to be the common case,
actually), the principal should ask the object whether the principal has
each set of roles in relation to the object; first "deny", and then "allow".
Sigh. This all sounds like dramatic overkill for the PEAK application
profile. In an application based on a business object model, permissions
almost invariably can be modelled relative to either a global role or a
local one, with role-to-permission mappings defined per-class. And in the
cases where that doesn't work, a custom storage mechanism for the roles and
permissions is needed anyway!
So, I think we should call YAGNI on any attempt to reproduce the full range
of Zope 2 or 3 security features. Instead, the overall delegation pattern
should allow you to create your own set of dynamic security features, if
you so desire. So PEAK will implement a three-way delegation mechanism,
wherein a "security manager" will ask the principal, the principal will say
"yes, no, or maybe", and if it's "maybe", the manager will ask the
subject. All three participants will be replaceable or changeable to play
their parts differently. The default/common case supported will be a
per-class permission-role map, coupled with a method that checks whether
one of a set of given "local roles" applies. If any default permission or
role definition support is added to 'peak.model', it'll be based on this
approach, but will be overrideable.
zope.app.traversing won't work for us in some areas. It expects to use
context wrappers to find things' parents. I think we want to use
peak.naming for this kind of thing,
anyway. zope.app.traversing.namespaces, however, is cool. It does parsing
for things like /fooSkin;ns=skin/ that let you "escape" the normal
traversal path off into another virtual namespace. This is handy for
distinguishing between a container's contents (e.g. a DM's contents) and
its views/methods. z.a.t.n provides default handlers for these namespaces,
but doesn't require that you use them. It's singleton-based also, and it
might be nice to have it contextual, but that's probably a YAGNI. How many
namespaces are you really going to want to have, anyway? The cool thing is
that by registering our own namespace handlers, we can bypass the normal
zope.app services for determining views, skins, resources, etc., if we need to.
The principal impact of replacing zope.app.traversing will probably just be
that we have to supply our own replacement for zope.app.pagetemplate.engine
as well, so that ZPT will use our traversing components. We might want to
do this anyway because z.a.p.e uses restricted builtins for ZPT, and we'd
probably just as soon use raw Python built-ins, since we won't have
Items for further investigation
Internationalization -- looks cool at first glance, but don't know much
about it yet
ZCML -- will we want to use it for anything, or will attempting to use it
cause a huge number of unneeded packages to be sucked in? For that matter,
are there any hidden gotcha imports in the parts of zope.app we're
interested in? I haven't seen anything so far, but...
UI tools -- can we use "menus"? how about "forms"? Views, skins, layers,
resources... all those goodies that are configured via ZCML... We'd have
to hook the ZCA to replace its services for finding these things if we want
true integration. But if we do, then we may limit interoperability...
??? -- more to come.
More information about the PEAK