Forum OpenACS Development: Permissions Hierarchy

Collapse
Posted by Tom Jackson on

Fourty Seven privileges are created in acs_object_grantee_priv_map (actually just virtual rows in a view) per object for every site wide admin for a default full install of OACS. I don't think this is directly any problem, but looking into this, I noticed that directly granting 'read' permission on an object resulted in granting a rather unexpected combination of permissions:

object_id grantee_id privilege
4998      7177       read
4998      7177       acs_reference_read
4998      7177       news_read
4998      7177       cal_item_read
4998      7177       calendar_on
4998      7177       calendar_show
4998      7177       calendar_read

What this says to me is that the read privilege is way too general to grant on an object. It seems that each object should have its own privilege sub-hierarchy to avoid these extra rows. However additional entries in the hierarchy will result in the admin users getting even more rows in the view.

The reason I started looking at this is for a way to streamline permission checking. The pl function permission_p is an all inclusive check, that in some cases leads to unwanted results.

Imagine an admin logging in and trying to assume the role of a less privileged user. It seems like there is a need for at least two additional permission checking functions. One will check directly granted permissions, actual entries in acs_permissions. The second will query the above referenced map for expanded privilege checking: privileges with children. This check is one step in the current permission_p check.

Collapse
Posted by Stephen . on
Why does an admin assuming the role of another user require two different permission checking functions? Wouldn't the user_id just be substituted? And why would you split the functionality of the two new functions at the directly granted/inherited permissions level? What unwanted results are you getting from the current set-up? So many questions!
Collapse
Posted by Don Baccus on
I've been thinking about this myself.  It's not really that "read" is too general, because all of the packages involved treat their "special read" privilege as being a synonym for plain old read (that's what defining a child privilege on a one-one basis does - as opposed to a one-many parent privilege like "admin" that grants you read, write, etc in one fell swoop).  If "read" were really too general than each package would assign subtly different semantics to each of their custom "read" privs.

I'm glad they don't, it would be damned confusing!  As it is, only the existence of all these custom "read" perms is confusing.

Anyway ... this topic's come up before but it's worth talking about again as 4.5 nears release.

Here's my conclusion:

1.  Having 500 different "read" privs is bogus, confusing to the implementor and deadly to any attempt to build an generic UI page.  Currently I can assign "cal_item_read" to a "news" object, which is every bit as silly as it sounds.

2.  The hierarchical mechanism should be restricted to defining one-to-many privs like "admin".

3.  Privs should be scoped by object type to a subtree of the object tree.  Pervasive, generic perms like "read" should be bound to "acs_object" (and therefore applicable to all subtypes of "acs_object", too).  Most packages only need generic perms, those that define their own should do so only when the new perm has a different semantic meaning than perms already available to it via perms scoped to the object's parent types.  The CR, for instance, might define "publish" as a perm and all CMS-style packages using the CR could make use of it.  They don't all need their own and there's no need to allow the user to assign the "publish" term to a "user" object (for example).

4. Cutting down the number of different params and the use of the hierarchy would certainly have no negative effect on performance, and possibly could improve performance.  It certainly meets the "do no harm" sniff test in this regard.

5. Scoping to the object_type hierarchy would make the generic admin perm UI immediately more useful.  Rather than being presented a long list of mostly irrelevant perms to choose from, you'd just get the perms that can legally be bound to your object.  Then the only confusion would be the mega-list of parties to assign as grantees that also appears on the page.  Fixing that is beyond the scope of this post :)

This is all post-4.5 stuff.  The scheme I have in mind would provide an easy upgrade path (just scope all perms in an existing 4.5 installation to acs_object) and flexibility for future development.

Since it's all post-4.5 stuff my plan has been to put together a proposal after 4.5 is released, though I've talked about such ideas to a few people here and there during casual conversation and the reaction thus far has been generally favorable.

Collapse
Posted by Tom Jackson on

Stephen,

The reason I would like, and will likely write a new direct permission checking function, is that it is much more efficient to do this type of checking, and provides more relevent information, in certain cases.

Here is an example. I am working on a new ecommerce package that I am calling Merchant System. In this package, customers are acs_objects which are mapped to users. A user can be mapped to multiple customer objects, and a customer can be mapped to multiple users. Each user is assigned a privilege in respect to a customer object, and all objects created by users for a particular customer have a context_id equal to the customer_id. All I need to do to verify that a particular user can see/edit/delete customer information is available by looking in the acs_permissions table. The user is validated against the customer by only checking the user to customer mapping table. Permission_p can still be used, but the specific design of the application allows more efficient checking.

So the crazy thing is that admins can do anything anywhere regardless of permission checks involving permission_p, it is like being root and happily deleting everything.

I see the point of Don's idea of scoping the permission check. To me it makes sense that an admin should have free reign somewhere in the system, but when they wander down to application land, their rights should be cut.

Collapse
Posted by Don Baccus on
Unfortunately, avoiding permission_p when possible will yield a high return in terms of performance if you've got a lot of objects, users, and permission rows.

There are places in the toolkit that already do this, and as we get more experience with the toolkit in larger and busier sites we'll undoubtably be introducing more such abstraction-breaking queries in the future.

Giving an admin free reign on permissions by allowing them to assign a perm to an object where the package writer didn't anticipate it is pretty meaningless, I think.  If the need arises it will be due to a bug in the package or some sort of design error, and making such an assignment meaningful will require some code hacking anyway.

So I don't think there'd be any loss of meaningful functionality as a result of adding scoping such as I describe.

Collapse
Posted by Stephen . on
It is not an error that an admin user can do whatever he or she pleases, it works that way through careful design!  I think the definition of admin could use some tweaking though...

I presume by admin you mean 'user with admin privilege on the root object' (object_id 0).  The permission system is so felxible that you can define your own more limited 'admin' to suite your purposes.  For example you might create tom_read, tom_write privileges for your tom package, and add tom_read and tom_write as a child privilege of tom_admin, and tom_admin as a child privilege of admin. Anyone with tom_admin is the admin for the tom package, nothing else.

Your subsite might use more than one package, so you might decide to create a group 'Tom's Susbsite Adminitators' and assign the tom_admin, news_admin and bboard_admin privileges to that group. Obviously now you can create subsite admins by adding users to the new group.

There are probably very few users of your system who are site wide administrators, perhaps just yourself, but by making all privileges ultimately a child privilege of admin, and all objects ultimately defer permission checking to object_id 0 (for those that do defer), it's possible to have a site wide adminsitrator (root user).  Of course, you don't have to assign that privilege to anyone if you don't want to.

Sure, if you have to do it you have to do it (you've tested it and it's too slow?), but by breaking the abstractions the permission system provides you increase the chance of introducing subtle bugs.

Collapse
Posted by Don Baccus on
<blockquote><i>It is not an error that an admin user can do whatever he or she pleases, it works that way through careful design!</i></blockquote>
I'd love to see some empirical evidence that being able to assign "calendar_write" to a glossary term resulted from "careful design".
<p>
More than one ex-aDer I've talked to about the current use of the permissions system has agreed that it is butt-ugly stupid.
Collapse
Posted by Don Baccus on
Oh, two additional points:

1. I'm well aware of how the permissions system works, and what it lets you do.  The fact that I disagree with how it is currently (ab)used in the toolkit doesn't mean I don't understand how it works.  You don't have to be brilliant to recognize stupid, and stupid is how it is used today.

2. The notion of scoping I propose doesn't reduce the flexibility one iota.  Scoping "tom_admin" to "Tom's subsite" would allow you to use it exactly as you describe, it would only prevent you from assigning it to "Jerry's subsite", a *good* think since doing so would be a NOP anyway.

3. Your example of "tom_admin" more or less proves my point.  You introduce this in your example because you want to introduce semantics that aren't provided by the handful of perms defined when acs-core is isntalled.  As I mentioned above that's *exactly* the case when it makes sense to introduce a new perm.  Introducing a new perm with exactly the same semantics as existing perms does nothing but clutter the name space.

But with scoping at least the clutter would be managable...

Collapse
Posted by Don Baccus on
Actually, with the object_type scoping I'm suggesting you wouldn't be able to restrict a perm to "Tom's subsite".  But nothing but good taste would prevent you from scoping such a perm to acs_object.
Collapse
Posted by Stephen . on
The example above demonstrates how it's possible to use the permissions system to create administrators with some subset of power, addressing Tom's concern that permission_p is ineffective because 'admins' can do whatever they like. It has nothing to say about scoping privileges to object types which has already been discussed, except that it uses the default style of privilege creation used throughout the toolkit.
Collapse
Posted by Tom Jackson on

Actually, I know that I can create a child_privilege and this is what I thought I should do, but this does not in any way address the problem that I am talking about, which is:

  1. more about a user being able to assume different roles in a system. Maybe it is only top level admins which have this problem, but in general the hierarchy as used causes this problem.
  2. about the automatic creation, through views, of completely bogus direct permissions, and I haven't even checked the other types.

Maybe the idea of a hierarchy of privileges is the true problem. I completely understand the hierarchy of objects, using the context_id, etc. But what does the hierarchy of privileges really get us? I think that figuring out if a user is an admin, and of what type, could be easier that the use of the magical object 0 (zero). Think about it, this system assigns every possible privilege on every object created to the superuser. This is obviously total nonsense. If you are going to grant every privilege, why grant any? There seems to be a crucial difference between the use of context_id and the privilege hierarchy. Context_id explicitly passes privileges up, whereas the privilege hierarchy automatically passes privileges down.

Also, just to complain a little, I don't really see why my idea of a shortcut of checking for direct permissions is a loss of the abstraction. The abstraction was lost already long ago when we realized that it didn't work for a reasonably small set of objects, and some extremely bright individuals rewrote the pemission_p procedure. My suggestion is to simply test one arm of this procedure in the case where direct permissions are assigned, and the designer wants only direct permissions to apply. In my particular case I think it makes perfect sense: I aggregate all privileges around a single object type by using the context_id of sub-object to point up, I assign privileges to this object for a group of users. Permission_p would still work to check if user x has right to read sub-object a, but a direct check is much quicker, and is exactly what is implied by the initial assignment.

The problem is, I don't want to create new sematics in a package I want to release for general use. If it is worth holding off on this hacking, speak up and I'll just use permission_p.

Collapse
Posted by Don Baccus on
<blockquote><i>Also, just to complain a little, I don't really see why my idea of a shortcut of checking for direct permissions is a loss of the abstraction.
</i></blockquote>
If you're referencing my comment, all I meant was that I think the original aD concept was to abstract out permission checking into the package API.  As I mentioned, you're not the first to break away from it by querying permissions (actually views on permissions) directly and we'll be seeing more of that in the future, without doubt.
<p>Of course I could be wrong and direct queries on the views may've been "acceptable" in the designer's view from day one.
<p>
It's absolutely necessary that we do this when it makes sense in order to keep performance up.
<p>Scoping in the way I've been thinking about would cut down on the number of rows in the permissions table among other things (looking back at your first post) because only those permissions scoped to an object's type would be added not every child perm in the system.
<p>I'm not quite seeing why the current system doesn't let you assume the role of a less privileged user ... what's missing is a way to assume their user_id (i.e. run permission_p as though you're them not you) but that's all, no?
Collapse
Posted by Don Baccus on
But what does the hierarchy of privileges really get us?
In *my* opinion (not shared by everyone, obviously) the hierarchy should only be used to lump privileges, the "one implies many" case.

The implementation - assign every permission in the system to the magic "0" object - is grotty but let's separate out implementation from the design semantics for a bit. If we think more about how we want the semantics to work a better implementation may fall out of it.

The current semantics allow one to create permissions that the system administrator does *not* have - all you need to do is to intentionally or forgetfully not add the child relationship between your perm and the system admin. I'm working on a client site where they actually want admins for portions of the site that the sitewide admin won't have, so I think we probably do want to maintain this.

If not, though, and if we're willing to keep the "magic admin party" object, permission_p could just check for that party and return true always.

By joining with the hierarchy table you could just grant admins the parent ("admin") perm to the object rather than all the kids. But with scoping and more judicious use of permissions (i.e not creating an "object_type_read" perm for damn near every object type in the toolkit) the number of rows added per object for the admin would be cut down tremendously and doing this assignment might be better than doing the join with the hierarchy table in terms of checking performance.

Collapse
Posted by Tom Jackson on

Don, I was going to agree with you on the one implies many point, but I think that there may be a slight difference. I keep wanting to think of some sort of tree structure. The one descendant case is a special case of multiple descendants. I think the main problem I have with this system is that it is mimicing a hierarchy. For instance does LDAP work by assigning every possible privilege? I don't think so. Instead it (probably) works its way up from the bottom looking for a right. A few, maybe a number, of targeted queries are made to determine access rights. If the system is simple, the number is limited, but I would guess that the number rises very slowly in relation to database size. I'm just wondering: is it that difficult to generalize the logic of 'who can do what to this'?

Maybe a better place to start is with other systems. What is possible, how do they do this type of thing? The OACS assumes infinite complex groupings, and makes everyone pay for it up front. From my experience, this works for large systems if you only check one permission per page. To me this resulted from good (hehe, my) design, but actually I have only worked with relatively well defined systems. I think the designers invisioned a hurd of managers constantly asking for this or that: 'Can you do that while scratching your ear?'. Although it is nice to think in the abstract, it is also nice to have a fast system. Which one do you think users will notice first?

Oh, Don, I think we are pretty much having the same problem: it works, but it looks weird, something just isn't right. I like the sound of scoping, but I'm not sure how that exactly works. Maybe something similar to the security_inherit_p flag for the context_id thing? Not really I guess. More like scoping in acs 3.x? I also like the idea of starting a new privilege tree.

Collapse
Posted by Don Baccus on
Well, I haven't thought out my scoping notion 100%, thus far it's just an idea floating around, an idea motivated in part by wanting to make the generic admin UI for permissions usable.  Scoping to object types would give that UI page a way to present exactly the list of permissions which the object type implementor makes use of, rather than a list of every permission in the system.

The problem with "querying up the tree" rather than "shoving all the child perms in the table" is query expense.  We already have this gawdawful problem with the complex views to generate various parties to check against ("registered users", "group members" etc) which are sometimes well-optimized and sometimes not by Oracle and/or PG depending on phase of moon and other arbitrary criteria.

Joining with the hierarchy table is one way to "query up the tree".  This might not be terribly expensive if more judicious use were made of the namespace, i.e. there'd be many fewer rows in the hierarchy table and the join would be fast.

I'm glad we're having this discussion because clearly permissions need some thought, and I certainly don't claim to have any silver bullets in my revolver, just one idea that may be useful.

Collapse
Posted by David Walker on
I thought recursive queries would at least partially solve the querying up the
tree problem.  Are they still too expensive for heavy use?