[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...


Publishing
----------

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).


Security
--------

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 
this.  :)

'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 
object.


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.


Security Model
--------------

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 
unwrapping point.

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 
traversal.

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 
permission.

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.


Path Traversal
--------------

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 
TTW-edited pages.


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 mailing list