Improving Support for End-User Programmable Security Objects
(such as "generic" user databases and "rule-based local roles")
Note - this document is still a work in progress. Please do
not make inline comments in the proposal - use the
UserProgrammableSecurityObjectsDiscussionx to make comments.
Phillip J. Eby - email@example.com
Expanding the dynamic nature of Zope often means a need to create components which interoperate with the Zope security system. Sometimes, these components act as suppliers of security data as well as consumers of it. User databases, for example, and objects which provide information about local security roles. However, when these objects are intended to be designed and maintained through-the-web, and thus execute user expressions under the restrictions of the Zope security model, new challenges arise.
Specifically, the current implementation of the Zope security model has two flaws which can snare code into (wholly unnecessary) infinite recursion:
Executables must be owned by a user object which comes from within the ZODB, which means that a user-programmable, root-level user database will keep asking itself for information about a user which it must ask itself whether it can access.
The new Zope 2.2 ownership model requires a place for ownership to "bottom out" so that user databases themselves can have owners. The previous solution was the creatable-by-super hack, which creates further problems in the ownership model, since user databases which involved components which followed the security model (e.g. GenericUserFolderx and LoginManagerx) then had to suppress ownership characteristics or else be unusable as a root-level user database. In addition, the initial existence of only a single Zope user who cannot actually do anything except create a UserFolderx, can be troubling to new users of Zope and at best is inconvenient for experienced ones.
User.allowed() currently looks for all the roles possessed by a user in relation to an object, regardless of the roles the security manager is actually interested in. In the event that a user-programmable local roles capability is in effect, such a facility will end up asking itself whether it has the right to determine whether someone has the right to something, and so on. All this, even if the user has all the access needed without the benefit of local roles.
Make it possible for other "root" level users to exist outside the ZODB as a standard feature of Zope, fixing both the "bottoming out" problem and the installation bootstrap problem. The natural place to place these additional users would be the "access" file which currently supplies the superuser object. The file already has to be created, and is already read by Zope during startup.
Second, adjust the implementations of the User allowed() and getRolesInContext() methods to stop searching for additional roles, if they have already found roles that grant the user the requested level of access to a thing. (This should also effect a performance gain in situations where local role computation is expensive.)
Impacts of Proposed Solution
The "rootUsers" can own things
They appear to be normal members of the root UserFolderx, except that they
do not appear in its contents as they are not stored there.
It is not necessary for a user database to support the rootUsers; however
if the top-level user database of a Zope installation does not support
them, they will not be usable in that installation.
User folder objects must be changed to support the rootUsers dictionary.
User databases using the existing name==super.getUserName() protocol are
unaffected; they simply will not provide access to the new rootUser objects.
Objects can define/override "get_local_roles_for_user(user,findroles=())" to implement rule-based local roles for a user object. The method returns a sequence of role names. If the findroles parameter is non-empty, the method is only required to return role names which are listed in findroles, although it is allowed to list whatever it likes.
The primary risk of fixing the first problem is scope creep. Specifically, users may begin wanting to be able to change the "access" file while Zope is running, expect these root users to show up and/or be editable in the Zope UI, etc. This risk can be largely mitigated by declaring the access file to be for "special users", and perhaps ultimately even renaming the file to "specialusers" to make this status clear.
The primary risk of fixing the second problem is that code may exist that relies upon the old implementation behavior. This is highly unlikely, however, since local roles are relatively new in Zope history, and most class developers tend to subclass from the standard implementation in the Zope core. The Zope core never uses getRolesInContext() except from the allowed() method, and it is possible to add the short-circuit behavior without changing the existing behavior in the event that some other code does exist that calls getRolesInContext() expecting a complete accounting of those roles.
The scope of the project is to make it possible for end-user programmable security providers to be developed cleanly in Zope without special-case hacks (such as creatable-by-super and the UnownableOwnerx) to work around the recursion problems.
This would be done by 1) making it possible to have additional "root" users located outside the ZODB, thus bottoming out the ownership hierarchy, and 2) making minor adjustments to reduce the implicit recursion in, and improve the encapsulation of, the Zope local roles machinery.
Patch to implement root users in the access file, to short-circuit local roles searches, and allow objects providing local roles the opportunity to inspect the user object directly to implement local roles. (Patch completed and submitted to Collector.)
Updates to the Zope access file editing tool, and Zope installation docs to explain the purpose of the "access" file as being for special users.
Updates to interface/API docs to reflect the additional interfaces provided by the patch.
Phase-out of "creatable-by-super" in the Zope core and in existing user databases as support for the root users is added to those databases.
Reference implementations of root-user access in a top-level user database, and computed local roles support (in LoginManagerx and ZPatterns, respectively).