8. Access Control
8.5. Sets - Granting rights based on relationships
Sets are best illustrated via examples. The following sections will present a few set ACL examples in order to facilitate their understanding.
(Sets in Access Controls FAQ Entry: http://www.openldap.org/faq/data/cache/1133.html) Note: Sets are considered experimental.
8.5.1. Groups of Groups
The OpenLDAP ACL for groups doesn't expand groups within groups, which are groups that have another group as a member. For example:
dn: cn=sudoadm,ou=group,dc=example,dc=com cn: sudoadm
objectClass: groupOfNames
member: uid=john,ou=people,dc=example,dc=com member: cn=accountadm,ou=group,dc=example,dc=com
dn: cn=accountadm,ou=group,dc=example,dc=com cn: accountadm
objectClass: groupOfNames
member: uid=mary,ou=people,dc=example,dc=com
If we use standard group ACLs with the above entries and allow members of the sudoadm group to write somewhere, mary won't be included:
access to dn.subtree="ou=sudoers,dc=example,dc=com"
by group.exact="cn=sudoadm,ou=group,dc=example,dc=com" write by * read
With sets we can make the ACL be recursive and consider group within groups. So for each member that is a group, it is further expanded:
access to dn.subtree="ou=sudoers,dc=example,dc=com"
by set="[cn=sudoadm,ou=group,dc=example,dc=com]/member* & user" write
by * read
This set ACL means: take the cn=sudoadm DN, check its member attribute(s) (where the "*" means recursively) and intersect the result with the authenticated user's DN. If the result is non-empty, the ACL is considered a match and write access is granted.
The following drawing explains how this set is built:
Figure X.Y: Populating a recursive group set
First we get the uid=john DN. This entry doesn't have a member attribute, so the expansion stops here.
Now we get to cn=accountadm. This one does have a member attribute, which is uid=mary. The uid=mary entry, however, doesn't have member, so we stop here again. The end comparison is:
{"uid=john,ou=people,dc=example,dc=com","uid=mary,ou=people,dc=example,dc=com"} & user
If the authenticated user's DN is any one of those two, write access is granted. So this set will include mary in the sudoadm group and she will be allowed the write access.
8.5.2. Group ACLs without DN syntax
The traditional group ACLs, and even the previous example about recursive groups, require that the members are specified as DNs instead of just usernames.
With sets, however, it's also possible to use simple names in group ACLs, as this example will show.
Let's say we want to allow members of the sudoadm group to write to the ou=suders branch of our tree.
But our group definition now is using memberUid for the group members:
dn: cn=sudoadm,ou=group,dc=example,dc=com cn: sudoadm
objectClass: posixGroup gidNumber: 1000
memberUid: john
With this type of group, we can't use group ACLs. But with a set ACL we can grant the desired access:
access to dn.subtree="ou=sudoers,dc=example,dc=com"
by set="[cn=sudoadm,ou=group,dc=example,dc=com]/memberUid & user/uid" write by * read
We use a simple intersection where we compare the uid attribute of the connecting (and authenticated) user with the memberUid attributes of the group. If they match, the intersection is non-empty and the ACL will grant write access.
This drawing illustrates this set when the connecting user is authenticated as uid=john,ou=people,dc=example,dc=com:
Figure X.Y: Sets with memberUid
In this case, it's a match. If it were mary authenticating, however, she would be denied write access to ou=sudoers because her uid attribute is not listed in the group's memberUid.
8.5.3. Following references
We will now show a quite powerful example of what can be done with sets. This example tends to make OpenLDAP administrators smile after they have understood it and its implications.
Let's start with an user entry:
dn: uid=john,ou=people,dc=example,dc=com uid: john
objectClass: inetOrgPerson givenName: John
sn: Smith cn: john
manager: uid=mary,ou=people,dc=example,dc=com
Writing an ACL to allow the manager to update some attributes is quite simple using sets:
access to dn.exact="uid=john,ou=people,dc=example,dc=com"
attrs=carLicense,homePhone,mobile,pager,telephoneNumber by self write
by set="this/manager & user" write by * read
In that set, this expands to the entry being accessed, so that this/manager expands to
uid=mary,ou=people,dc=example,dc=com when john's entry is accessed. If the manager herself is accessing John's entry, the ACL will match and write access to those attributes will be granted.
So far, this same behavior can be obtained with the dnattr keyword. With sets, however, we can further enhance this ACL. Let's say we want to allow the secretary of the manager to also update these attributes. This is how we do it:
access to dn.exact="uid=john,ou=people,dc=example,dc=com"
attrs=carLicense,homePhone,mobile,pager,telephoneNumber
by self write
by set="this/manager & user" write
by set="this/manager/secretary & user" write by * read
Now we need a picture to help explain what is happening here (entries shortened for clarity):
Figure X.Y: Sets jumping through entries
In this example, Jane is the secretary of Mary, which is the manager of John. This whole relationship is defined with the manager and secretary attributes, which are both of the distinguishedName syntax (i.e., full DNs). So, when the uid=john entry is being accessed, the this/manager/secretary set
becomes {"uid=jane,ou=people,dc=example,dc=com"} (follow the references in the picture):
this = [uid=john,ou=people,dc=example,dc=com]
this/manager = \
[uid=john,ou=people,dc=example,dc=com]/manager = uid=mary,ou=people,dc=example,dc=com this/manager/secretary = \
[uid=mary,ou=people,dc=example,dc=com]/secretary = uid=jane,ou=people,dc=example,dc=com
The end result is that when Jane accesses John's entry, she will be granted write access to the specified attributes. Better yet, this will happen to any entry she accesses which has Mary as the manager.
This is all cool and nice, but perhaps gives too much power to secretaries. Maybe we need to further restrict it.
For example, let's only allow executive secretaries to have this power:
access to dn.exact="uid=john,ou=people,dc=example,dc=com"
attrs=carLicense,homePhone,mobile,pager,telephoneNumber by self write
by set="this/manager & user" write by set="this/manager/secretary &
[cn=executive,ou=group,dc=example,dc=com]/member* &
user" write by * read
It's almost the same ACL as before, but we now also require that the connecting user be a member of the (possibly nested) cn=executive group.