[Date Prev][Date Next] [Chronological] [Thread] [Top]

Re: Granting rights based on relationships

Here's the syntax for what I'll call "Sets" for lack of a better term. Look at it as an overview, as the details are easy to change. For example, I'm using "." where it might be better to use "/", as then OIDs (which have .'s in them) can be used in place of attribute names. Also, the literal op ("[ ]") below can hold any value, but maybe we want to limit it to DNs, or provide some way to specify the syntax of the literal.

In the examples, I've abbreviated DNs for readability. You should be able to get the idea, though.

Pardon the pseudo-BNF:

  <set> :=        <base>
                | "(" <set> ")"
                | <set> <conj> <set>
                | <set> "." <attribute> "*"
                | <set> "." <attribute>
  <base> :=       "this"
                | "user"
                | "[" <any text> "]"
  <conj> :=       "&" | "|"
  <attribute> :=  any attribute name

The base "this" refers to the target directory object to which the set applies. The base "user" refers to the directory object for the currently connected user. The base "[xxx]" can be arbitrary text, or can refer to an object by its DN.

Use parentheses to override the usual operator precedence. The operator "*" has the highest precedence, followed by ".". "&" and "|" have the lowest, and equal precedence. Operations of equal precedence are evaluated left-to-right unless parentheses dictate otherwise.

The conjunction operators, "&" and "|", combine the sets that are described by their operands. "&" will produce the intersection of the two operand sets, while "|" will produce the union.

The operator "." produces the set of all values for the given attribute for all objects in the given set. The closure operator "*" will recursively add the values of the attributes for the given set.

For example, say the target object ("this") is "cn=Resource" and the user object is "cn=User". Then:

    user | this :  resolves to the set { "cn=User", "cn=Resource" }

If there is a group "cn=Group" with members "cn=User" and "cn=Other", then:

    [cn=Group].member & user :  resolves to the set { "cn=User" }

If another group, "cn=Group2" has members "cn=Group" and "cn=Person", then

    [cn=Group2].members* :  resolves to { "cn=User", "cn=Other", "cn=Person" }

Note that literal bases (things enclosed in [ ]) do not have to be DNs. So, to test if the current user speaks English:

    user.language & [English]

which resolves to the empty set if the user does not speak English, otherwise it resolves to the set { "English" }. Of course, when the target object is something like a document, a more useful template would be:

    this.language & user.language

Here's a way to specify all of the immediate managers of the target object's owner who are in either of the (possibly nested) groups "cn=Marketing" or "cn=Sales":

    this.owner.manager & ([cn=Marketing].member* | [cn=Sales].member*)

Normally, for access permissions, we care only if there is some relationship of the current user to the resulting set. So, the above set would be more useful as:

    this.owner.manager & ([cn=Marketing].member* | [cn=Sales].member*) & user

We could make this more power (and more complex and costly to compute) by allowing base sets to be built from LDAP filters. This is something to consider, because the combination of filters and sets (which have little overlap in what they can express) is very powerful.

Questions and comments welcomed.