logo  
www.thekramers.net: Users, Groups, and Privileges
   
Home page
About

Boston Linux
Freshmeat
Slashdot
Meltdown

Powered by Red Hat

Powered by Apache

Powered by PHP

Powered by MySql

Get Speakeasy.net internet access

NOT powered by Microsoft


 

Users, Groups, and Privileges

David KramerVersion 1.4 12/14/2001

Some software products require very fine granularity of authorization, such that different groups of people have access to different parts of the software, or access to different sets of data within it. Additionally, these systems also require the authorization to be easily changed as the list of users and their functions change.

This paper describes a flexible, scalable, and secure authorization scheme with three main objects: users, groups, and roles. It was written in preparation for a website with those needs, but would be just as applicable to other software products, and is independent of the authorization scheme of the underlying operating system. The benefits of such a scheme are:

  • It is easy to assign a set of privileges to a person.
  • It is easy to assign the same set of privileges to a group of people
  • It is easy to transfer privileges from one person or persons as responsibilities change in the real world
  • Privileges can be assigned without changing the software.

Users

The user table has one row (record) per user in the system. It holds the uid, the password, and whatever personal information you want to track. If there's a lot of other attributes to track, though, you may want to use a parallel table (with a one-to-one relationship between them) to keep access speedy.

Groups

The group table has one row per group. A group is a set of one or more users. This table holds the gid and group name. All members of a group will have the privileges assigned that group (more on that later). Groups will usually closely match the logical groups of an organization. In many situations, though, there will be several authorization levels for each real-world group. For instance, if there is a real-world group called accounting, the group table might have accounting, accounting.edit, and accounting.admin The accounting can view all accounting data, the accounting.edit group can view all accounting data and add/delete/modify content, and the accounting.admin group can view all accounting data, add/delete/modify content, and add/remove users from the group.

You will note the extra accounting groups all start with the same name, then a period, then another name. Using this technique, you can create a heirarchical relationship of groups, which may make maintenance easier. You can also create groups like accounting.assets.realestate.

Privileges

The privilege table has one row per privilege. It holds the pid and privilege name. A privilege is something you want to let certain groups of users do, but not others. It may entail modifying data, or getting certain reports, or executing some maintenance task. Privileges can have the same dot-separated heirarchy as I described for groups above.

How it all ties together

There are two more tables:
  • usergroup: This table controls what users are in what groups. Each row holds a uid (from the user table) and a gid (from the group table). There is one row per user/group combo.
    Lets say there are three users. Two of them are in one group, and the third was in a second group. The first user is also a member of a third group. The table would look like this:
    uidgid
    user1group1
    user1group3
    user2group1
    user3group2
  • grouppriv: This table controls what groups have what privileges. Each row holds a gid (from the group table) and a pid (from the priv table). There is one row per group/privilege combo.
    Lets say there are three groups. Two of them can add events to the calendar, and the third can do that, plus add new calendars, and the third group can view private information on users. The table would look like this:
    gidprivilege
    group1calendar.event.add
    group2calendar.event.add
    group3calendar.event.add
    group3user.viewprivate

Here is an "Entity Relationship Diagram" of how the tables connect to each other (note that the arrows do not match the correct ERD notation since my drawing tool could not do that).

In some environments, yet another layer is added, called roles. Roles are sets of privileges. This lets you easily assign a set of privileges to a set of groups. Instead of having a grouppriv table, you would have a role table, a rolepriv table, and a grouprole table. Roles are assigned privileges, and groups are assigned roles. In most environments, this is overkill, so I won't go into it past this paragraph.

Implementation

Typically there will be a function to call anywhere in the program to see if the current user is allowed to perform some action. It may look something like this pseudocode:
print($firstName);
print($lastName);
if(userAuthorized($userID,"user.viewprivate"))
{
	print($email);
	print($phoneHome);
	print($phoneWork);
	print($address);
}
This function would look in the tables to see if that user is in any groups that have the privilege "user.viewprivate". If so, then the personal information will be printed. If not, it will be skipped.

Some extensions to this scheme make maintenance easier. For instance, a pid of ALL in the grouppriv table could be hard-coded to allow all privileges for users in the group with the ALL privilege, so the developers can develop and maintain the system easier. So if the grouppriv table has a row like this:
gidpid
rootALL
then any member of the group root will have all privileges. No need to add more rows as new privileges are added to the system, as any call to userAuthorized() with a user in the root group and any privilege will return TRUE.

Also, gid of ALL in the grouppriv table could be hard-coded to allow all users to have that privilege. You may ask why someone would do this. The reason is that you may want to have some capability in the system open to all now, while reserving the right to restrict it later. Without this extension, you would have to go back into the system and change it to check for user authorization. With this extension, you merely adjust the tables so that only certain groups have that privilege instead of ALL, without any changes to the software. So if the grouppriv table has a row like this:
gidpid
ALLuser.self.edit
then any call to userAuthorized() with the user.self.edit privilege for any user will return TRUE, and all users will be able to edit their own user data.

An even further extension is to allow a special hard-coded gid of ANY in the grouppriv table, which will match in cases where the user isn't even logged in, or has no uid.