[Zope-dev] looking for ideas on access control...

Phillip J. Eby pje@telecommunity.com
Fri, 06 Aug 1999 09:47:16 -0500


At 03:49 PM 8/6/99 +1000, Anthony Baxter wrote:
>For an application I'm building, I'm looking at trying to figure out
>a simple and robust method of doing access control - there's a bunch
>of different users who each have access to certain objects (stored in
>Oracle), and they should get different access based on which object ids
>they are trying to access. 
>
>What I want to be able to do is have something I can call in 
>standard_html_header which does something like
>* get AUTHENTICATED_USER.
>* get REQUEST['object_id'].
>* lookup in SQL the rights that this user has over the object with
>object id object_id.
>* set the roles of the user for this transaction, to either 'anonymous/none',
>'readonly', 'readwrite', or some other variation, and let the permissions
>on the appropriate DTML and SQL methods control what they can do.
>
>What's the mechanism for editing the roles of a transaction? Is it even
>doable?
>
>Could I simply use a UserDb, add 'object_id' to the list of arguments
>for sqlListUser, and make the SQL magic supply the roles? Will this get
>called for each transaction?

Yes.  That'd probably be the easy way, and would work so long as you have
object_id in your request information.  The only tricky bit is that if you
carry around state in your query string like that, your HTML generation is
going to be complicated by it.  (Since you'll need to put the object_id
into either a cookie or the query string.)

If you use the "Zope Zen" way of accessing SQL objects by URL, e.g:

/IDS/Data/Activities/Get/IDS-987036/display

You'll need to use the 'hard way' instead.  (You also may need to go the
'hard way' if the question of granting access is more complex than dropping
in a string of roles from an SQL db.)  The 'hard way' is to give all
objects retrieved from the SQL database a 'pluggable brain' which provides
a __allow_groups__ attribute.  This attribute must be an object which
supports a validate() method as documented in the ZPublisher docs, similar
to the one which the UserDb object has.

In the application where I do this, the validate() method queries a db for
the user object, which itself has a pluggable brain.  The user object is
then asked to check the REQUEST authentication data and then verify that
the user has rights to the target object (which is available from the
PARENTS array in the REQUEST object, or as aq_parent of the
__allow_groups__ object if it's Acquisition-based).  These checks are done
by the validate() method calling the user object's 'authenticate' and
'hasRole' methods.

The user object's hasRole() method checks general role information first,
to see if the user is of a type that is granted blanket access of some kind
to the sought-after roles.  If so, it returns true.  Otherwise, it checks
whether the target object has an 'allowsUser' method.  (Note: this is my
invention - not a Zope-standard protocol; there may be a standard for this
in Zope2 that I am not yet aware of.)

If the target object has an 'allowsUser' method, the user object passes
itself and the desired roles to that method, thus delegating the access
decision to the pluggable brain of the original object for which acess was
requested in the first place.  (Whew!)  Only now, all that the object has
to do is look up its relationships to the user, either in its original
fields returned from the DB, or by doing a query to the appropriate join
table(s).

And yes, all this stuff will get run on every hit/transaction, so setting
reasonable caches on these things is a Very Good Idea.