Methods to shift your enterprise.TM  

Solution Pattern

Identity Grouping

Facilitates the assembly of users into a collection that has a unique name and maintains a set of distinct characteristics, such that all the users assembled by the collection have all the characteristics of the collection.

 

 

 

[image]

 

 

Background

You have many clusters of users of your system, where users in a given cluster all need to be assigned identical characteristics. Managing each of the users individually will make for an administrative headache. If you have to change one characteristic of a given cluster of users you would have to manually modify each user’s characteristics individually. This is both time consuming and extremely error prone.

 

Value and Benefits

What we need is a way to associate every user that is part of a given cluster to a collection that can be given the desired characteristics, rather than the characteristics being given to the individual users. Thus, when you modify the characteristics of the collection all of the users that are maintained by it automatically receive the changes by way of association. Assembling users together has the added advantage of providing further identity to the users in the collection, and the collections serve as a convenient point of reference regarding who fits in where, and what they are like.

In identity and access management and in traditional system security models the collections of users are called groups. Each group has a unique name. Users assembled by a group are called members.

The characteristics given to the group are generally permissions, but might not be strictly limited to access and other security concerns. Individual users may be members of none, one, or many groups. While the groups themselves are discussed here, the roles and permissions are treated in Role (page #). It should be stated here that a role is different from a group. A role is similar to a group in that it can hold a number of members and characteristics on its own. The characteristics are generally associated with access rights and privileges.

Note that I favor roles over adding permissions to a group (or user for that matter) because the enterprise deals more favorably with a line-of-business approach to access management than to a traditional access control list (ACL) approach. Therefore, you will not find any treatment of permissions here or in the User (page #) pattern. See the Role (page #) pattern for all access control constraint mechanisms. If you must support permissions on users and groups then you may add them easily enough to the implementations of the two related patterns. There are a plethora of permissions and ACL-based security mechanisms on the market, such as those built in to Unix™, Windows™, and Novell™.

Just about everyone would rather deal with maintaining the characteristics of groups of users rather than maintaining the characteristics on individual users. Just because groups are at play does not mean that the characteristics of some individual users cannot be maintained separately.

 

Putting It to Work

A group is an entity with a unique name and a set of associated entities. The entities in the set are members of the group. A member is any domain object that implements a specific interface, such as MemberIF. This implies that both users and groups can be members of a group. Thus, there may be groups within groups.

When creating a new group, make sure that that name given to the group is unique. This is the same constraint as is placed on usernames. You will need to ensure that every user and/or group added to a group as a member is unique. The members can be enforced as unique if you use a collection that supports the behavior of a Set. This may be accomplished using an implementation of the java.util.Set interface, for example, to gather the users and groups in memory before persisting them. Once persisted all element entities are guaranteed to be unique:

 

 

 

[code]

 

If you are using an LDAP-based identity management system then the group is created as a simple LDAP entry. Here is the directory hierarchy that I might build for my company:

 

 

 

[table]

 

The members entries under each group are of the LDAP class groupOfUniqueNames. This class has an attribute that I use to hold the directory context strings for each member of the group. You only use the group’s members entry to find out who its members are. If you find a member that you are interested in then you use their member context to find out where their personal information can be found elsewhere in the directory. For example, if I were a member of the admin group the members entry would have included in its uniqueMember attribute values my uid:

 

 

 

[code]

 

 

 

We would then use my uniqueMember attribute value to look up my personal information in the people directory hierarchy.

If you are using a relational database then each group could be represented by a row in a GROUPS table. It would make use of GROUP_MEMBERS table to hold unique group elements.

With either approach I recommend the use a persistent domain objects with a framework that knows how to save, find, and read the groups. This is the approach I use in my examples.

 

Dynamic Groups

The notion of a dynamic group allows you to create groups of users on the fly based on certain qualifying criteria (one or more). The group is assembled from the search results using the query criteria that qualify users to be part of a given group.

This has advantages over manually assembling groups. An administrator, for example, can simply provide a query that is used to find all users with a specific set of characteristics. This has an advantage over having to manually place all desired users directly into the group. On the other hand, it does require that each user that will qualify for a group will have to be given the proper characteristic. So here’s a rule of thumb for using dynamic groups: Consider only using a dynamic group when the set of characteristics that the group is qualified by is guaranteed to be part of the user’s characteristics. That is, administrators will not have to provide the extra set of characteristics, since the users themselves or some application operation will have automatically created it. Otherwise administrations may as well manually assemble groups.

A dynamic group could be made up of all users with the same first letter of their last name:

 

 

 

[code]

 

 

 

The DynamicGroupFactory would be responsible for interpreting the meaning of the parameters and building the group. In this case it would look under all user accounts for all sir names that start with the letter ‘A.’ Of course this simple example assumes that LDAP is the identity management storage of choice. A more natural way of getting such a dynamic groups would be as follows:

 

 

 

[code]

 

 

 

Class DynamicGroup would have the built-in query criteria for qualifying the group based on the group type. The group type has the name "User#sirName". The qualifying criteria is "sn=A*". No doubt the administrative provisioning console would allow the criteria parameter types for each dynamic group type to be specified when the dynamic group type is created.

 

Examples

The interfaces used to define the contract between significant players in this pattern are GroupIF and MemberIF:

 

 

 

[code]

 

 

The interface DirectoryDomainObjectIF is the one described in the overarching Identity and Access Management (page #) pattern. It allows implementers to provide their persistence manager of choice. MemberIF implementers have to produce a unique identity, so that each member of the group can qualify as a unique entry. Members of a group may be users. If I chose to support groups within groups I could also define an isGroup() method, which would provide a distinction from isUser().

Here is the full (and predictable) implementation of GroupIF:

 

 

 

[code]

 

 

There’s nothing really special going on here. The group has a unique name and a unique identification number (optional). It’s also concerned with the maintenance of its members Set instance. After the members are updated in memory they will be persisted out to a directory service. In the case of my example, OpenLDAP is used and it is accessed via JNDI. Class GroupDirectorySession implements the interface that serves to allow the GroupIF domain objects the in behalf of groups, namely DirectorySessionIF. The following code snippet shows how groups are saved and updated:

 

 

 

[code]

 

 

 

The save() method creates a new group under the entry named ou=groups,dc=jubatus,dc=com. The group itself is given a distinguished name made from the group’s common name appended to the general groups entry, such as: cn=admin,ou=groups,dc=jubatus,dc=com. Within this group are created the members distinguished-named entry:

 

 

 

[code]

 

 

 

All totaled, the following shows and distinguished names in hierarchical format using the admin group as an example:

 

 

 

[code]

 

 

The cn=members entry contains a multi-value attribute named uniqueMember. When saving this attribute for the first time I simply instantiate the attribute and add one value for each uniquely referenced people (user) entry in the group. I then create the new members sub-context:

 

 

 

[code]

 

 

A slightly different approach is used to update the uniqueMember attribute when one or more unique names are added to and/or removed from it (during group maintenance, for example). After the attribute is built in the same way it was during its creation, the following methods are used:

 

 

 

[code]

 

 

 

I create a ModificationItem and set its type to REPLACE_ATTRIBUTE. This indicates that the attribute already existing in the directory should be replaced part and parcel with the attribute just created. Finally I use the DirectoryService method modifyAttributes(). This method arranges for the method of the same name to be invoked on the JNDI DirContext object, which actually effects the change to the new uniqueMember attribute.

See the overarching Identity and Access Management (page #) pattern example for a listing of the JNDI utility class, DirectoryService.

 

Consequences

The following competing forces may be considered when selecting an implementation approach.

  • Assigning Roles to Groups: Some identity and access management systems do not assign security users and groups to roles. Some systems assign roles to users and groups, where the users and groups act as a container of both permissions and roles. Frankly this is a matter of perspective. If the notion of users and groups existed long before role-based access control was introduced, then it may be easier not to change the composition of users and groups by adding roles to them. It would be easier to make roles contain the legacy objects. However, if your design is starting fresh then it may be more advantageous to assign roles to users and groups. It may be much more optimal to ask the in-context user object if it is in a specific role by name, than to fetch the specific role by name and ask if the in-context user is in the role. If possible favor the approach that is most natural to the administrators and developers directly accessing the domain objects and API. Assigning users and groups to roles has the overall least impact on the overall structure of a domain model.

  • Groups Within Groups: Some find it advantageous to accumulate members in finer-grained group collections and then collect those groups into one outer group, rather than to capture them all in one self-defining container. After all, it is easier to add an all-encompassing group later than to redefine several smaller groups into one new one. Note, however, that the detailed semantics of groups within groups are not intuitively obvious. For example, what if mutually exclusive permissions or roles cause a conflict when one inner group may have a permission or play a role but the outer group cannot? Is this an error or does one role’s existence overrule the denial of service defined by another one? These conflicts do not occur when using single-level groups.

 

Frameworks and Tools

  • OpenLDAP: I used OpenLDAP to test my examples. You may migrate toward a commercial implementation, but you may also find this open source product to suit your needs.

  • Sun J2SE JDK Implementation of JNDI: I used the Sun implementation of JNDI provided in the freely available JDK 1.4. This includes the com.sun.jndi.ldap.LdapCtxFactory JNDI factory and provider classes.

  • JXplorer: I find this open source tool from Computer Associates to be very useful when examining the changes I make to LDAP directories. It is freely available for download under the CA Open Source License.

 

[EOF]