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.