Using the MidCOM CLASS-style privileges
2005-07-27 11:09

Yesterday I added the so-called CLASS-style special privileges to the MidCOM ACL system. Their usage is not that hard, but since they are falling a bit out of the regular ACL "scheme", it can be a bit confusing at first. This Blog entry gives a quick introduction into them.

The special privileges introduced yesterday are primarily a way to introduce new, more complex assigne rules then the simple privilege-assignee-content-object mappings. For reasons of flexibility, the assignee for these privileges does always consist of an acciocative array, containing at least the key type.

The only special privilege known at this point is the CLASS privilege, which assigns a privilege to a given user which applies generally to all objects of a given class or subclass thereof. These privileges are therfore only assignable to users and groups, and behave very similar to the SELF-style privileges.

Merging notes

As briefly outlined in the docs, CLASS privileges are scoped between global (SELF) privileges and content privileges. In the DBA layer, they are applied when creating top-level obejcts for example. Look again at the scope graph:

 ^ larger scope     System default privileges
| Privileges for USERS/ANONYMOUS
| Root Midgard group
| ... more parent Midgard groups ...
| Direct Midgard group membership
| Virtual group memberships
| User
| CLASS style per-class global privileges
| Root content object
| ... more parent objects ...
v smaller scope Accessed content object

Obviously, for top level object creation, there is no content object whatsoever, so the CLASS privileges are the end of the merging chain in those cases. It is important to understand however, that CLASS privileges are used always, not only for top-level obejcts, which were the premiere reason for introducing them. The only exception to this rule are the general can_user_do checks which do not have a class accociated with them.

Setting CLASS privileges

Above I stated, that special privileges always have an array of an assignee. We look at this using a practical example:

 1: $user =& $_MIDCOM->auth->get_user($some_user_id);
2: $assignee = Array (
3: 'type' => 'CLASS',
4: 'identifier' => 'midcom_baseclasses_database_topic'
5: );
6:
7: $user->set_privilege(
8: 'midgard:create',
9: $assignee,
10: MIDCOM_PRIVILEGE_ALLOW
11: );

As you might have guessed, this call allows a given user to create root topics. The magic is in the lines 2 to 5, which defines an class-assignee. From that point on the given user will now have that privilege merged in if and only if the objects we are talking about are MidCOM DBA topics or subclasses thereoff.

Developer's note: Internally, such privileges are translated into the parameter name CLASS:$identifier.

Reading CLASS privileges

When reading privileges using get_privileges on any user or person storage object, the system returns a regular privilege record with the assignee array outlined above, when print_r'ing you will see something like this (constants were resolved):

Array
(
[MIDCOM_PRIVILEGE_NAME] => midgard:create
[MIDCOM_PRIVILEGE_ASSIGNEE] => Array
(
[type] => CLASS
[identifier] => midcom_baseclasses_database_topic
)
[MIDCOM_PRIVILEGE_VALUE] => MIDCOM_PRIVILEGE_ALLOW
)

Checking against CLASS privileges

When querying privileges for actual content objects, there is no change in behavoir for you.

It gets interesting if and only if you want to query top-level privileges, without having a content object available. For this, the method signature of can_user_do has been changed (documentation update pending) to include a way to specify a class:

function can_user_do($privilege, $user = null, $class = null);
function require_user_do($privilege, $message = null, $class = null);

The additional class parameter is what you want, you specify a class name, not an actual object there. As long as you check against the current user, you can still keep $user null to check against the current user.

The most important point here is that the class referenced in those arguments must be default constructible as the check will create an instance of that class dynamically; otherwise the is_a() check that is used to identify all applying privilege sets won't work.

Lets put this together to a final, short example, based on the topic privileges shown above:

1: $_MIDCOM->auth->can_user_do('midcom:create', null,
2: 'midcom_baseclasses_database_topic');
3:
4: $_MIDCOM->auth->require_user_do('midcom:create', null,
5: 'midcom_baseclasses_database_topic');
6:
7: $_MIDCOM->auth->can_user_do('midcom:create', null,
8: 'some_subclass_of_the_midcom_topic_baseclass');

All three cases check if the current user has the permission to create topics generally, even if they are root topics. The third case assumes that the class mentioned is a subclass of the topic baseclass of the MidCOM DBA core.