Forum OpenACS Development: Ideas for using groups to minimize custom permission
Some people have mentioned it would be a nice idea to have a site-wide news administrator, for example, that can administer any instance of the news package, but otherwise has no admin privileges.
One way to do this without a "news-admin" privilege is to create a "news_admins" group when the news package is installed, and grant the admin privilege over a news package instance when the instance is created.
The missing piece is a consisten user interface to handle the maintainence of memberships to the "news_admins" group.
I thought for a second that relational segments under Registered Users might work, but it doesn't really work correctly. You might have news admins as well as admins for other package types, and the relational segment data model is really designed to handle one mapping per user/group/role.
If you wanted to use relational segments, you would just create an 'admin' group, and then use relational segments to carve out sub admin tasks: 'news', 'forum', etc. Of course, you would need to create new membership_rels for each segment, I think.
> memberships to the "news_admins"
Maybe the standard group membership UI? (That could of course be improved, but that's another topic)
This will create a bunch of 'special groups', per subsite I guess, no? Where and how will they be maintained and identified?
That is can one user have two different membership relationships with the same group.
It works, each relational segment to a specific group has a different rel_type. I think they are all derived from membership_rel or composition_rel. You would use the membership_rel and create derivatives of this. You add members to the group using these rels and add permissions using the segment (which is a party).
First, you frequently want to grant admin on all *content* of an object, e.g. all bugs in a bug-tracker, without granting admin on the bug-tracker itself. Or admin on all forums and threads, without granting admin on the forums package itself.
Also, the 'grant admin on all news items' has come up time and again.
So how about this: Have 3 types of grants:
- grant on the object itself, but not the child
- grant on the children, but not the object itself
- grant on all children of a certain object_type, but not the object itself.
Before you say "permissions is slow already, this will just make it slower", we have a design at hand which is known to speed things up considerably:
Instead of having one row per (object_id, grantee_id, privilege), like you do today, have a table with a single row per (object_id, grantee_id), and with one column for each privilege in the system.
Of course, that number can fluctuate, but if we just create about 500 spare columns with generic names, that should be good enough for the time being. Plus we want to get rid of custom privs, not add to them.
Branimir has some experience with this, and can help us implement it for OpenACS, and he claims that it made permissions checks a zip.
Lars, Huh? You mean there isn't a handy pl procedure to do it, not that the data model doesn't support these three 'grant types', right? Writing this procedure would be just as easy or difficult with your proposed model, wouldn't it? I'm not sure what the extra columns would accomplish other than making it impossible to join (easily) against the table. I think since Don's recent reorganization of the base tables you can do a join to do bulk permission checks, although I haven't tried it myself.
- I want to grant 'admin' on all news items to user x.
Create a site-wide "News" group, etc. -- see above.
My conceptual design proposal:
Instead of thinking of this as granting privs on *packages* think about it as granting privs on *objects*.
What you want to do is grant 'admin' on all objects of type 'news_items'.
One possible implementations:
Add a column 'object_type' to the acs_permissions table, which defaults to 'acs_object', menaning this permission is granted on all object types.
Change the permissions check procs/views to also check whether the object in question is a subtype of the value of object_type column in the acs_permissions table.
... there are other possible implementations, if you agree with me on the conceptual design.
We should just be able to add columns when we create a new privilege, i think.
Also using application groups etc should mimimize the need for custom privileges. We just need good documentation on how to accomplish the common cases.
I am not sure about the 3 types of grants. Would it actually enter the privileges into the table, so the grants are just really different pl/sql functions?
The reasoning for my design was to limit administration of an object type under a context. The context would most likely be a subsite. It could be a folder instead. The key is that there is a group that "owns" the folder by having a certain relationship to the folder. The application then grants all rights to that group or relational segment within that group.
The key is that we don't have an ownership concept in openacs, so I guess this is one way to do it.
I think it is a good idea to also be able to grant permission by object type, so the concepts are complementary.
I inteded this as a solution to your originally stated problem, though, so I see them as two possible solutions to the same problem.
In my proposal, you could grant "admin" on objects of type "news_item" on a subsite, and it would cascade to all specific news_item package instances as usual, and thus be scoped to both the subsite (or wherever you decide to grant it in the containment hierarchy currently implemented by context_id) *and* it will also let you scope to specific object types.
If you do the "object type proxy object" and grant permissions on that, then you'd be granting the permission site-wide, which, to me, doesn't really cut it -- I want to be able to host multiple separate sites on one instance/DB for ease of maintenance.
I do see that your proposal works with the existing permissions system, whereas mine proposes to make a change to that existing system to fulfill a requirement, which has surfaced time and again.
I specifically am not very hot on the thought of having packages do this automatically, so a default install will have a group per application, and maybe even a group per application per subsite. Or maybe even more? What if you want to grant "write" or "create" or "read" or some other privilege? Then you'll want to have a "News Readers", "News Creators", "News Writers", and "News Administrators" groups for each application x subsite on your site. Was that what you were thinking?
Lars, in response to your last paragraph: you only need one group, probably per mounted instance (for instance privileges), or per subsite (for subsite wide privs). But each grant 'write', 'read', 'admin' would need a separate rel_type (only three for an entire OACS installation). Checking for the membership type in a certain group would be the same as checking the permission to do an action. But I think having the 'write' membership on a news package would not imply I could change someone elses posting, that would be handled by direct permissions on the news object itself. But if a certain membership is required to access a page where administrative functions are available, you don't need to look at direct or indirect permissions on objects.
At any rate, if you can create a site which somehow knows the difference between data for one subsite and the next, it is obvious you can construct a unique proxy object, or group membership, hopefully automatically, that can be used to do what Dave suggests.
This would be a fairly major change for client code that queries against the standard permission views, though, as is recommended for best performance.
The "admin all news_items" problem is most easily solved by ignoring supposed weaknesses in the permissions system design entirely (not to mention supposed optimizations of the implementation of that design). The weakness in this case lies in the object model design, as has been discussed several times over the past couple of years:
Object types should be OBJECTS. Then you just grant someone "admin" on the "news_item" object and presto, everything works.
Awhile back someone was working on "object proxies" for objects, i.e. just binding an object to each object_type in the system (of type "object_type_proxy" I imagine). This would be a lot easier than redesigning the object model, at least from the upgrade script P.O.V.
One key element that slows down the permissions system is the fact that we don't have one row for each (object_id, grantee_id, privilege) that applies to each object_id, grantee_id pair.
Rather we just have rows for privileges that are set explicitly.
In other words, if we set "admin" we don't expand the table with entries for "read", "write" etc. We join against a hierarchy table.
This is necessary, unfortunately, for the current semantics. In the one-row-per-object model we just can't set the "write" column (or bit) for an object when we set the "admin" privilege. Or more specifically we can't blindly delete the "write" privilege when we delete the "admin" privilege - the grantee may have "write" explicitly as well as implicitly from having been granted "admin".
When I considered my bitstring implementation (one bit per perm, as mentioned above identical in concept to Branimir's notion) I couldn't see any straightforward way to maintain the current semantics. You end up wanting to store one row for each explicit grant on an object which sets not only the granted priv but all the child privs. There goes part of the supposed win ...
Maybe Branimir has figured out a simple way to track this while only storing one row per object, if so, he should share. If not, then we really don't have an alternative implementation that's a lot faster in hand, do we? Instead we have a potentially faster implementation of a permissions system with different semantics.
On the other hand the current datamodel can also be further denormalized to expand the hierarchy properly and to maintain the current semantics.
However, with a large number of permissions and complex hierarchy the tables would grow HUGE.
So I chickened out when I last visited the problem. Huge tables are more a space problem, though, not necessarily a significant performance problem given the log2(N) performance characteristics of queries on simple tables with proper indexes.
We need to get rid of custom privileges as much as possible. This will speed permissions checking without any change to the underlying datamodel at all, and will also make it more reasonable to consider various other denormalizations or changes to the underlying datamodel. "foo-read" descended from "read" which implements standard "read" semantics is a waste conceptually and in practice.
I've mentioned several times that sort_key hierarchies could be moved into a separate table, and used as a mapping table. One advantage would be that you could create more than one security context for an object (or more than one of any type of hierarchy). This would allow you to add a "parent" object for all news items and use that item for permissions, while the package object would be on the same level, but not related to the "news admin" security object.
So, in my opinion, the problem is with the limited implimentation for the security context, which only allows a single hierarchy. There are still a lot of things to consider: would you need separate hierarchy and hierarchy type tables, so you could find all the "security" hierarchies, or would you store the hierarchy type in the mapping table to avoid extra joins?
After posting yesterday, I realized Lars must mean a change in the permission proc. I think another, simpler alternative is to create a special object and use permissions on that object as a proxy for "admin on all news objects". This could be setup as a package parameter, couldn't it? This is much more flexable, and you could wrap it up into a proc. There is no way to enforce future applications will honor any permission checks, but this has always been the responsibility of the developer, which seems like a good thing.
That is why I proposed my original solution. Doing a standard permission check on the intended object will work fine.
My idea was a way to use the existing permissions system. That is what seems to be confusing, it what to do with all the flexibility. I just wanted to provide a way to model a requirement using the existing system.
Dave, our discussion seemed to reach a conclusion, I think Lars has brought up a wider topic, maybe we should start another thread on that? Or maybe Lars wants us to reconsider?
At any rate, the idea of object proxies is something I use for every page I write. Checking permissions, or even maintaining permissions on every object, usually ignores the fundamental organization of data and user roles. I think the flexibility of OACS permissions is very helpful, but writing an application which makes efficient use of permissions still takes some thought, this is always my first job with any package.
Right, the key is how to setup permissions for a particular application. I'd like to develop a catalog of solutions for common applications. I'll start a new thread about that when I am ready.
we already had some discussons around this subject, and as a result P/O uses OpenACS permission them in several ways (http://www.projop.com/doc/intranet-core/permissions.html). However we are facing some additional use case.
For example with projects I frequently have to ask: "Who is allowed to see this project" in order to show the list of authorized users. According to the permissions documentation (tediously explained) I shouldn't ask this question due to performance reasons, is that right?
The other problem appears in the context of our HP project, where we have to deal with some 20.000 users and 20.000.000 documents/folders. Permissions on folders are set by means of certain user groups. The task is to filter the result set of an Oracle Intermedia search query to show only the documents that a user is allowed to see. I think we all agree that the current permission system can't handle this because the size of the permission table due to the denormalization triggers, right?
The solution to the first issue was to use a custom relationship between projects and users, avoiding to use the permission system.
The solution for the second issue is to store the explicit grants (user group - folders) and to create a "denormalized cache" between user (because we know the user of the search query) and projects. An entry in this cache says: "There is atleast one file in this project which the user can read."
This way Oracle can rapidly discard all files from the result where the user has no access permissions at all. The remaining files have to check one-by-one using hierarchical queris, but that's a relatively small number of objects to check.
Finally, with respect to Daves "news-administrators": This is exactly the standard case at Project/Open, for example with the "Accounting" role (manages all finalcial objects) etc. or a "Sourcing Manager" who can add and modify "Freelancers" and "Provider" and their business objects.
The solution that we have chosen (see link above) consists of special groups "Accountants", "Resource Managers", ... and application specific permission procedures (im_invoice_permissions $user_id $invoice_id view read write admin) that check for the users membership in the specific groups (via acs_rels) or if the user owns system-wide privileges (via the OpenACS permission system).
I see you need an email/password to get access to
the P/O documentation at
Email/password is: "mailto:email@example.com"/"sysadmin";.
I have yet to see a specific example where this level of extra complexity is needed. I do plan (someday, eventually) to move context_id out of acs_objects since it's only used as something to hang triggers onto (all the real work is done in a hierarchy table), but that's more for efficiency than anything else.
Among other things we don't *use* sortkeys for tree hierarchies in Oracle, and I'd like to avoid it. In fact, if PG's hierarchical queries are efficient (I think they may appear in 7.5, implemented in SQL standard fashion) I would suggest that eventually we may switch PG to use them, though we'd want timing experiments first of course (sortkeys are one reason why object creation is slower in PG than in Oracle, a good reason to be very cautious in suggesting we switch to sortkeys in Oracle so we can maintain a forest rather than a tree).
On the other hand, the context hierarchy table doesn't use sortkeys, but rather parent/child relationships, in both PG and Oracle, so once we fully separate the permission hierarchy from the physical object tree hierarchy modelling multiple hierarchies for the context structure would be possible, even if (IMO) undesirable.
As I mentioned above, the problem Dave's brought up
could be solved by having a news-admin group with admin privs on the "news_item" object type proxy object (something we should have for this and other reasons, I think). Aplications are free to pull other tricks such as use the package object as a proxy object for specialized permission checking if necessary.
I want to think about this some more to try to understand all the suggestions.
I do see that your proposal works with the existing permissions system, whereas mine proposes to make a change to that existing system to fulfill a requirement, which has surfaced time and again."
Well ... first off Dave's original post talked about "site-wide" as meaning "an entire OpenACS installation", and the object type proxy object approach does indeed meet this need.
Your comment ... well, subsites were designed to be SUBsites, not to implement what in essence are a bunch of entirely different sites within one OpenACS installation. I realize a fair number of people want to use the subsite mechanism to implement a set of unrelated sites, personally I question whether or not it's worth the bother. Pushing subsites into a space where they meet that need involves a lot more than some of the permissions issues that are being discussed here.
And I'm very dubious about suggestions that we make the permissions system more complex. If changes can be made that work with the existing datamodel that's one thing, as I can see how to continue to incrementally improve the performance without much trouble. Since I plan on moving context_id out of the object table, multiple hierarchies can also be implemented without much pain if this were to prove useful (and it probably would to do some of the stuff we're discussing, now that I've thought about it more ... Tom may be right, as much as the additional complexity bugs me).
But changes in the design adding additional functionality that require a fundamental change in the datamodel are likely to work against scalability - and permissions scalability is still an issue in OpenACS, let's face it.
Don, I agree completely that adding complexity for no reasonable advantage is very not good, especially concerning acs_objects and permissions. My suggestion of having multiple context hierarchies is really just an consequence of my thinking that a generic object hierarchy table would, overall, benefit the toolkit. Any time a developer wishes to establish a certain hierarchy (parent-child, security context, etc.) they might be able to do it by adding a new hierarchy type and using a generic table. I have never had a problem with the currently available setup, but many times developers have used the context_id for something besides the security context.
From what I am imagining, when/if you move context_id out of acs_objects, that move will not in itself decrease the performance of the permissions system, since it will just substitute the join table. But maybe it will be moving somewhere else where it might increase performance, so the added complexity would not be worth the time and performance issues.
But we're going to have a REAL parent_id in acs_objects in 5.2 and context_id can be used for its real purpose: inheritence of permissions.
Now ... two quick points ...
1. If we only had PG and tree_sortkeys we wouldn't need a parent_id as the sortkey encodes the set of parents (and this leads to very quick queries when you want the set of all parents, faster than CONNECT BY as no internal joining is involved). But that's neither here nor their given our Oracle non-sortkey implementation and, IMO, the disadvantages of sortkeys in other areas.
2. Moving out context_id will get rid of certain triggers and, IMO, make the permissions inheritance API more obvious (at least there should be no excuse for confusion with a physical parent_id if we adopt this suggestion after I TIP it, oh, later this spring). It will allow multiple inheritance hierarchies with no extra cost in terms of query complexity/execution times I believe ... so we can decide on whether or not permissions would benefit at that time.
We are? Great!
You're not talking about package_id, are you?
But I'm glad that we agree on where to go.