Introduction into the ACL structures
The new ACL system being introduced into MidCOM provides ground-breaking changes to the Midgard World. This article describes the structure of the ACL in detail, as the mRFC 15 is very confusing at that point, I admit that. Before I update the mRFC I decided to write a bunch of articles covering one aspect at a time.
Attention: The contents of this article has been moved to the MidCOM API documents (official URL pending, current snapshot is available).
Privileges in general
A privilege record consists of three informations:
- The privilege name is an unique identifier for the privilege. The mRFC defines the syntax to be $component:$name, where $component is either the name of the component or one of 'midgard' or 'midcom' for core privileges. Valid privilege names are for example 'de.linkm.taviewer:do_something' or 'midgard:update'.
- The assignee is the entity to which the privilege applies, this can be one of several things, depending on where the privilege is taken into effect, I'll explain this below in more detail:
- On content objects (generally every object in the system used during 'normal operation'):
- A Midgard User encaspulated by a midcom_core_user object.
- A Midgard Group encaspulated by a midcom_core_group object or subtype thereoff.
- The magic assignee 'EVERYONE', which applies the privilege to every user unconditionally, even to unauthenticated users.
- The magic assignee 'USERS', which applies to all authenticated users.
- The magic assignee 'ANONYMOUS, which applies to all unauthenticated users.
- On users and groups during authentication (when building the basic privilege set for the user, which applies generally):
- The magic string 'SELF', which denotes that the privilege is set for the user in general for every content object.
- An array holding the two keys type and identifier, which are used for special permission assignments. Currently, these are supported:
- type=CLASS, identifier=$php_class_name: Use this to assign privileges generally to a given class. This is useful if you want, for example, grant a user the permission to create top-level objects.
- The value is one of MIDCOM_PRIVILEGE_ALLOW or MIDCOM_PRIVLEGE_DENY, which either grants or revokes a privlege. Be aware, that unsetting a privilege does not set it to MIDCOM_PRIVLEGE_DENY, but clears the entry completly, which means that the privilege value inherited from the parents is now in effect.
So much for this, I already see you looking with big eyes at me. Lets take this apart now. The most immediate question is probably Where the hell do I now assign privileges, and what happens then? To answer this questions we have to look at the privilege merging chain.
How are privileges read and merged
First, you have to understand, that there are actually three disctinct sources where a privilege comes from: The systemwide defaults, the currently authenticated user and the content object which is being operated on. We'll look into this distinction first, before we get on to the order in which they are merged.
Systemwide default privileges
This is analogous to the MidCOM default configuration, they are taken into account globally to each and every check wether a privilege is granted. Whenever a privilege is defined, there is also a default value (either ALLOW or DENY) assigned to it. They serve as a basis for all privilege sets and ensure, that there is a value set for all privileges.
These defaults are defined by the MidCOM core and the components respectivly and are very restrictive, basically granting read-only access to all non sensitive information.
Currently, there is no way to influence these privileges unless you are a developer and writing new components.
User / Group specific privileges
This kind of privileges are rights, assigned directly to a user. Similar to the systemwide defaults, they too apply to any operation done by the user / group respectivly throughout the system. The magic assignee SELF is used to denote such privileges, which can obviously only be assigned to users or groups. These privileges are loaded at the time of user authentication only.
You should use these privileges carefully, due to their global nature. If you assign the privilege midgard:delete to a user, this means that the user can now delete all objects he can read, unless there are again restricting privileges set to content objects.
Content object privileges
This is the kind of privilege that will be used most often. They are accociated with any content object in the system, and are read on every access to a content object. As you can see in the introduction, you have the most flexibility here.
The basic idea is, that you can assign privileges based on the combination of users/groups and content objects. In other words, you can say The user x has the privilege midgard:update for this object (and its decendants) only. This works with (virtual) groups as well.
The possible assignees here are either a user, a group or the magic assignee EVERYONE, as outlined above.
Be aware, that Midgard Persons and Groups count as content object when loaded from the database in a tool like net.nemein.personell, as the groups are not used for authentication but for regular site operation there. Therefore, the SELF privileges mentioned above are not taken into account when determining the content object privileges!
This is, where we get to the guts of privileges, as this is not trivial (but nevertheless straight-forward I hope). The general idea is based on the scope of object a privilege applies:
System default privileges obviously have the largest scope, they apply to everyone. The next smaller scope are privileges which are assigned to groups in general, followed by privileges assigned directly to a user.
From this point on, the privileges of the content objects are next in line, starting at the top-level objects again (for example a root topic). The smallest scope finally then has the object that is being accessed itself.
Let us visualize this a bit:
^ larger scope System default privileges
| Privileges for USERS/ANONYMOUS
| Root Midgard group
| ... more parent Midgard groups ...
| Direct Midgard group membership
| Virtual group memberships
| CLASS style per-class global privileges
| Root content object
| ... more parent objects ...
v smaller scope Accessed content object
Implementation notes: Internally, MidCOM separates the "user privilege set" which is everything down to the line User above, and the content object privileges, which constitutes of the rest. This separation has been done for performance reasons, as the user's privileges are loade immediately upon authentication of the user, and the privileges of the actual content objects are merged into this set then. Normally, this should be of no importance for ACL users, but it explains the more complex graph in the original mRFC.
The MidCOM core defines a set of core privileges, which fall in two categories:
These privileges are part of the MidCOM Database Abstraction layer (MidCOM DBA) and have been originally proposed by me in a mail to the Midgard developers list. They will move into the core level eventually, but for the time being MidCOM will control them. Unless otherwise noted, all privileges are denied by default.
- midgard:read controls read access to the object, if denied, you cannot load the object from the database. This privilege is granted by default, and superseeds the current ViewerGroups implementation.
- midgard:update controls updating of objects. Be aware, that you need to be able to read the object before updating it.
- midgard:delete controls deletion of objects. Be aware, that you need to be able to read the object before updating it.
- midgard:create allows you to create new content objects as childs on whatever content object that you have the create privilege for. This means, you can create an article if and only if you have create permission for either the parent article (if you create a so-called 'reply article') or the parent topic.
- midgard:parameters allows the manipulation of parameters on the current object if and only if the user also has the midgard:update privilege on the object. This privileges is granted by default and covers the full set of parameter operations (create, update and delete).
- midgard:attachments is analogous to midgard:parameters but covers attachments instead and is also granted by default.
- midgard:privileges allows the user to change the permissions on the objects they are granted for. You also need midgard:update and midgard:parameters to properly execute these operations.
Stuff like midcom:approve will go here once they are defined.
TODO / Ideas
- There is currently no way which allow system administratiors to set privileges local to a site(group).
- There is no assigne target that matches only unauthenticated users, something like ANONYMOUS.
- Do the same for authenticated / valid users, like ALLUSERS
- Create hierarchial privileges (f.x. midgard:user includes midgard:read).