Back to Groups Project

Groups Database Definition

Aims

This project will provide group querying and updating functionality to various tools, allowing for group ownership and permissions to be assigned.

Groups are fully hierarchical, allowing for open-ended nesting and mixtures of individuals and groups within any given group.

Client Program Usage

This project will be used as an information resource to client programs. It will provide a universal primary key for entities, allowing the programs to associate their own data with groups and individuals.

Clients will be able to write information into the groups definitions as well, so that clients such as the Schedule Manager can add new employees and put staff into appropriate groups. They will also be able to delete entities.

To prevent the deletion by one client of an entity used by a second I plan a registry of used entities. When a client registers the use of an individual or group, the central groups application will return an error to any other clients who attempt to delete said entity.

A barebones web application will allow an administrator to edit the individual, group and membership information directly.

Possible Clients

Platform

Database: The PostgreSQL database running on qna-dev.cac will store our data.

Language: The core application and the client modules will be written in Perl.

Location: This application will run on qna-dev.cac. A web interface for the administration of group information will also be available. Hopefully, we can also use this project to establish a production version on qna-help.

Program Structure

Client Module

Each client program will be given a Perl module, Groups.pm, to include. This module will export a simple API (described below) for the client programs to use. This API will be abstracted, allowing our eventual shift to LDAP if necessary. All API calls will be forwarded on to the Groups service.

Network Daemon

We will create a network daemon that will field client requests and commands. This will allow us to bypass the delay that would be incurred by a web service CGI launching for each request, and will provide better handling for multiple client connections. All transmitted data will be encrypted via the Perl Crypt:CBC module. This module is pure Perl, and is small enough to be included with each client. A shared key will be the basis of communication between server and client.

Database Design

The database contains five tables: entity, membership, useregistry, ipallowed, and function.

entity Table

The entity table will hold information on the individuals and groups in the database.

It contains the following fields:

Name Type Value Example
entityID int Primary Key 23465
name varchar Real Name Jimmy Dean
uwnetid varchar UW Net ID jdean
email varchar Email Address jdean@cac.washington.edu
is_group boolean Flag to indicate group status t
is_internal boolean Flag to C&C status t
is_deleted boolean Flag for deleted entries t

membership Table

The membership table contains all data that represents the membership tree. It contains the following fields:

Name Type Value Example
membershipID int Primary key 234
groupID int ID of the group which contains this membership 787
entityID int ID of the member 1234

useregistry Table

The use registry contains data that records the use of individual entities by various clients. It contains the following fields:

Name Type Value Example
useID int Primary Key 987
entityID int ID of the entity being used. 87
clientTag text Client Tag of the client who is registering use coverage

ipallowed Table

ipallowed stores the IP addresses that are allowed to make connections to the groups database. It contains the following fields:

Name Type Value Example
ipaddr varchar(15) One allowed ip address 140.142.184.82

function Table

Contains the names of functions which should be given to each client as they connect. It contains the following fields:

Name Type Value Example
functionid int Primary key 34
name text Name of one function to provide to client deleteEntity

Client API

All client programs will interact with the groups database using the following API.

This specification consists of five parts:

Data Items

The Groups database deals in three major items: entities, groups, and individuals. An entity is either a group or an individual. Groups contain entities. An individual represents a person.

Control Functions:

Data Queries:

Data Modifications:

Use Registration:

Examples

An example of some query functions:

use Group;

# create a new groups object
$grp = new Group("schedule manager");

# get some info about an individual
%indivInfo = $grp->getEntity('uwnetid' => 'mcrawfor');
print "Name: ". %indivInfo{'name'} . "\n";

# get a list of groups which mcrawfor belongs to
%groupList = $grp->groups($indivInfo{'entityid'});

# %groupList now contains a hash: keys are entityIDs for 
# the groups, values are entityHashes for the groups, so:

foreach $item (keys %groupList){
	print $groupList{$item}{'name'} . "\n";
}

# belongsTo provides boolean membership checking:
# Note that "3" is a groupID (found with groups)
if( $grp->belongsTo( $indivInfo{'entityid'}, 3){
	print "True";
}else{
	print "False";
}

# members gets a hash of entityHashes that belong to the
# given group. The optional second argument controls the
# depth to which the membership tree is traveled.
%members = %grp->members(3,1);

# %members now contains a hash like above, so:
foreach $item (keys %members){
	print $members{$item}{'name'} . "\n";
}

#disconnect
$grp->close();

Which prints:

Name: Miles Crawford
Student Staff
Leads
Routers
FaqMan Admins
True
Miles Crawford
Michal Guerquin

Some data modification examples:

## Create a new entityHash and add it to some groups:
%newEntity = (	'name'		=>	'Mr. Tux',
		'uwnetid'	=>	'tux',
		'email'		=>	'tux@cac.washington.edu',
		'is_group'	=>	0);

@initialGroups = ( 45, 12, 43 );

%errHash = $grp->addEntity(%newEntity, \@initialGroups);

if($errHash{'error'}){
  print "An error occurred: " . $errHash{'description'};
}else{
  $tuxID = $errHash{'description'};
  print "Success, entity $tuxID was added.\n";
}


## Add Tux to some new groups:
@moreGroups = ( 23, 34 );

%errHash = $grp->joinGroup($tuxID, @moreGroups);

if($errHash{'error'}){
  print "An error occurred: " . $errHash{'description'};
}else{
  print $errHash{'description'} . "\n";
}


## Tux leaves a group or two
@oldGroups = ( 23, 12 );

%errHash = $grp->leaveGroup($tuxID, @oldGroups);

if($errHash{'error'}){
  print "An error occurred: " . $errHash{'description'};
}else{
  print $errHash{'description'} . "\n";
}


## Delete a group
%errHash = $grp->deleteEntity(34); 
#error: Tux belongs to this group

if($errHash{'error'}){
  print "An error occurred: " . $errHash{'description'};
}else{
  print $errHash{'description'} . "\n";
}

Which prints:

Success, entity 94 was added.
Successfully joined groups.
Successfully left groups.
An error occurred: Entity '34' has current members. Can't Delete.

A use registry example:

## Register our use of Tux
%errHash = $grp->registerUse($tuxID);

if($errHash{'error'}){
  print "An error occurred: " . $errHash{'description'};
}else{
  print $errHash{'description'} . "\n";
}

## Delete Tux
%errHash = $grp->deleteEntity($tuxID); 
%#error: We've registered Tux

if($errHash{'error'}){
  print "An error occurred: " . $errHash{'description'};
}else{
  print $errHash{'description'} . "\n";
}

## Release our use of Tux
%errHash = $grp->releaseUse($tuxID);

if($errHash{'error'}){
  print "An error occurred: " . $errHash{'description'};
}else{
  print $errHash{'description'} . "\n";
}


## Try to delete that lovable penguin again
%errHash = $grp->deleteEntity($tuxID); #should work now

if($errHash{'error'}){
  print "An error occurred: " . $errHash{'description'};
}else{
  print "Success, tux is gone.";
}

Which prints:

Successful register.
An error occurred: Entity '94' is used by: schedule_manager. Can't delete.
Successful release.
Success, tux is gone.

Entity List Sorting:

Frequently the groups manager will return to you hashes full of entity hashes, and it may be useful to sort these lists by name or netid, etc. Here is an example subfunction for doing this by name:

sub sort_entity_list{
  my %orig = @_;
  my @ordered;

  @ordered = 
  sort { 
    lc($orig{$a}{'name'}) cmp lc($orig{$b}{'name'}) 
  } keys %orig;

  return @ordered;
}

Then, you can iterate through your entities in name-order:

%big_list =  $grp->allIndivs();

@order = sort_entity_list(%big_list);

foreach my $key (@order){
  print $big_list{$key}{'name'} . "\n";
}

Which prints:

Aardvark
Amos
Andy
Bob
...

Back to Groups Project