Forum OpenACS Development: Re: Best Practices for permissions, straw man

Collapse
Posted by Joel Aufrecht on
I may not have been sufficiently clear about my goal. I'm not proposing any changes to OpenACS. My sole goal in this thread is to produce fodder for a "How to Code Permissions in a 5.0 Package" chapter for the Engineering Standards section of the documentation. While we have documentation about how the permissions model works, we don't have instructions on how to use it. I'm also not (yet) aiming for tutorial-level detail.

My plan for doing this is:

  1. Describe a permissions model in sufficient detail that it makes sense and people can tell what I'm talking about
  2. Solicit terminology corrections so that my model matches what's already in OpenACS
  3. Adjust the recommended permissions model so that's it's a useful default model, including looking at non-OpenACS best practices to avoid re-inventing the wheel
  4. As a group, figure out the best way to implement the generic permission system in a new package, including one which uses acs_objects/cr_items, one which doesn't, and one which uses a blend. This task has several parts
    1. Figure out how to get maximum benefit from the toolkit with minimum new code. (Example: If I make the core table of my package an acs_object table, but add no other permission-specific code, how complete a permissions system do I get? What's missing, and how do I put it in? This is tutorial-level stuff)
    2. Figure out best practices for implementing permissions. (Examples: recommend how to set up rel_segs to group privileges, and *which* rel_segs with *which* default names to use to provide the generic model; cull the API for things we should deprecate; etc)

So, I think we're somewhere in step 2. Let me summarize what I think I've heard:

  • What I called EDIT, the power to change other people's objects (which includes constitution privs for seeing, changing, and deleting), is currently called WRITE. So where I proposed READ, WRITE, EDIT, we have READ, ??, WRITE.
  • A privilege is just a label - it does not have any intrinsic code or refer to any subjects or objects. When a priv is associated with a subject (a party) and an object (an object), the resulting record is called a permission.
  • Privs can be bundled together, so that three or four privs can all be assigned or revoked as a group. This is done by creating parent-child relationships between the group priv and the individual privs in acs_privilege_hierarchy. Thus, a priv can be a leaf or a node in a tree, but it's still just a collection of labels. (E.g., "create" could include "create forum," "create message," and "create comment," but that's literally all it is - three names. If a function tries to determine if party X can "create comment" on object Z, and party X can do "create", then party X can also do "create comment.")
  • Instead of assigning privs to an invididual person, you can instead assign privs to a group. (I'm less sure on this bit than on the others - please scrutinize.) Group is a node in the party hierarchy, so you can add and remove users and groups of users to groups. You could thus make an "editor" group and a "moderator" group with mostly non-overlapping permissions and put some people in both groups, or even put some people in a "super" group which is in both the "editor" and "moderator" groups.
  • An example of everything so far:
    • create a bunch of privs and create a parent priv called "edit"
    • create a group called "Editors" within your new package,
    • make a permission record that says "Editor has priv 'edit' on object '$package_id'"

Before looking at implementation issues, like Tom's point that an "add" priv must apply to a parent object since it can't apply to an object that doesn't exist yet, I want to bang at a few more details in our abstract "best practices" model:

  • Do we want to recommend a distinction between "read an object" and "be aware of the existence of an object"? One example is, you can often see subsites listed which you don't have the permission to enter. One solution is that, if you can't read/enter something, you shouldn't be able to see it on lists. The drawback to this is that sometimes you want people to see things so that they see how to ask for access. And sometimes you don't. So I see three choices for best practices:
    • If you don't have "read", you shouldn't see something in lists, and it shouldn't show up in counts
    • If you don't have "read," you can't drill into something, but it's still in lists and counts
    • There are distinct "read" and "see" privs. Showing a list of available subsites hinges on the "see" priv; clicking on that subsite and seeing the contents is requires "read" access (this is an illustrative example, not a suggestion to recode acs_subsite).
  • So what's the correct priv tree? My updated straw man:
    • "see own"
    • "read own"
    • "add"
    • "edit own"
    • "delete own"
    • "see other"
    • "read other"
    • "edit other"
    • "delete other"
    • 9 more privs of the form "grant X"
    • "read": "see other" and "read other"
    • "create": "add", "see own", "read own", "edit own", "delete own"
    • "write": "see other", "read other", "edit other", "delete other"
    • "creator": "create" and "read"
    • "editor": "read" and "create" and "write"
    • "grant 'read'"
    • "grant 'create'"
    • "moderator": "editor", "grant 'read'", "grant 'create'"
Collapse
Posted by Tom Jackson on

Joel, the problem of just adding terms, new privileges, sub-privileges, whatever, is that it doesn't solve any issue, and in fact, makes things worse. If 'read', 'write' and 'admin' have camellian like properties, subdividing these isn't going to help anything. The UI determines the meaning of this data.

Here is my analysis of the situation, expressed in code in my query-writer package.

An object is composed of attributes, so I divide up objects into attributes. You can only do a few things to object attributes: create, update, view. You can also delete an entire object, but I view this as delete of the object_id, so delete only applies to the object_id attribute.

To control access to an object I create roles. Actually in query-writer two roles are 'assumed': users who have admin on the security root context, are in the admin role, otherwise they are in the default role. Otherwise new roles can be created by creating a rel_segment and placing users in it. But what a role is allowed to do, is defined outside of the acs object system, it is part of query-writer. So first query-writer picks a role, then validates that a request to create, update or delete object attributes is possible within the role. Once this validation passes, for updates I require 'write' permission on the object, and for delete I require 'admin'.

Also certain roles can be limited to certain value choices for an attribute. For instance if an object attribute was 'publish_p', you might want to limit setting the value to '1' to the 'publisher' role. Although both the writer and editor would have write access to the content related attributes, they shouldn't have access to other metadata attributes.

I'm sure the workflow package addresses the same issues because it allows variable access to objects depending on a user's role defined in the workflow. But in the end this is necessary when you want to auto generate variable access to objects. If you can hand write the UI, you don't need a formal model to follow, and requiring one might be counterproductive.